diff --git a/SOURCES/ruby-2.1.0-Adding-Psych.safe_load.patch b/SOURCES/ruby-2.1.0-Adding-Psych.safe_load.patch new file mode 100644 index 0000000..bbe73f1 --- /dev/null +++ b/SOURCES/ruby-2.1.0-Adding-Psych.safe_load.patch @@ -0,0 +1,904 @@ +From 7ceafcbdf5bd2155704839f97b869e689f66feeb Mon Sep 17 00:00:00 2001 +From: tenderlove +Date: Tue, 14 May 2013 17:26:41 +0000 +Subject: [PATCH] * ext/psych/lib/psych.rb: Adding Psych.safe_load for loading + a user defined, restricted subset of Ruby object types. * + ext/psych/lib/psych/class_loader.rb: A class loader for encapsulating the + logic for which objects are allowed to be deserialized. * + ext/psych/lib/psych/deprecated.rb: Changes to use the class loader * + ext/psych/lib/psych/exception.rb: ditto * ext/psych/lib/psych/json/stream.rb: + ditto * ext/psych/lib/psych/nodes/node.rb: ditto * + ext/psych/lib/psych/scalar_scanner.rb: ditto * ext/psych/lib/psych/stream.rb: + ditto * ext/psych/lib/psych/streaming.rb: ditto * + ext/psych/lib/psych/visitors/json_tree.rb: ditto * + ext/psych/lib/psych/visitors/to_ruby.rb: ditto * + ext/psych/lib/psych/visitors/yaml_tree.rb: ditto * ext/psych/psych_to_ruby.c: + ditto * test/psych/helper.rb: ditto * test/psych/test_safe_load.rb: tests for + restricted subset. * test/psych/test_scalar_scanner.rb: ditto * + test/psych/visitors/test_to_ruby.rb: ditto * + test/psych/visitors/test_yaml_tree.rb: ditto + +git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@40750 b2dd03c8-39d4-4d8f-98ff-823fe69b080e +--- + ChangeLog | 24 +++++++ + ext/psych/lib/psych.rb | 57 +++++++++++++++-- + ext/psych/lib/psych/class_loader.rb | 101 ++++++++++++++++++++++++++++++ + ext/psych/lib/psych/deprecated.rb | 3 +- + ext/psych/lib/psych/exception.rb | 6 ++ + ext/psych/lib/psych/json/stream.rb | 1 + + ext/psych/lib/psych/nodes/node.rb | 4 +- + ext/psych/lib/psych/scalar_scanner.rb | 19 +++--- + ext/psych/lib/psych/stream.rb | 1 + + ext/psych/lib/psych/streaming.rb | 15 +++-- + ext/psych/lib/psych/visitors/json_tree.rb | 7 ++- + ext/psych/lib/psych/visitors/to_ruby.rb | 79 +++++++++++++---------- + ext/psych/lib/psych/visitors/yaml_tree.rb | 13 +++- + ext/psych/psych_to_ruby.c | 4 +- + test/psych/helper.rb | 2 +- + test/psych/test_safe_load.rb | 97 ++++++++++++++++++++++++++++ + test/psych/test_scalar_scanner.rb | 2 +- + test/psych/visitors/test_to_ruby.rb | 4 +- + test/psych/visitors/test_yaml_tree.rb | 4 +- + 19 files changed, 383 insertions(+), 60 deletions(-) + create mode 100644 ext/psych/lib/psych/class_loader.rb + create mode 100644 test/psych/test_safe_load.rb + +diff --git a/ChangeLog b/ChangeLog +index be56f61d3a19..e8ad02a53921 100644 +--- a/ChangeLog ++++ b/ChangeLog +@@ -3137,6 +3137,30 @@ + + * include/ruby/intern.h: should include sys/time.h for struct timeval + if it exists. [ruby-list:49363] ++ ++Wed May 15 02:22:16 2013 Aaron Patterson ++ ++ * ext/psych/lib/psych.rb: Adding Psych.safe_load for loading a user ++ defined, restricted subset of Ruby object types. ++ * ext/psych/lib/psych/class_loader.rb: A class loader for ++ encapsulating the logic for which objects are allowed to be ++ deserialized. ++ * ext/psych/lib/psych/deprecated.rb: Changes to use the class loader ++ * ext/psych/lib/psych/exception.rb: ditto ++ * ext/psych/lib/psych/json/stream.rb: ditto ++ * ext/psych/lib/psych/nodes/node.rb: ditto ++ * ext/psych/lib/psych/scalar_scanner.rb: ditto ++ * ext/psych/lib/psych/stream.rb: ditto ++ * ext/psych/lib/psych/streaming.rb: ditto ++ * ext/psych/lib/psych/visitors/json_tree.rb: ditto ++ * ext/psych/lib/psych/visitors/to_ruby.rb: ditto ++ * ext/psych/lib/psych/visitors/yaml_tree.rb: ditto ++ * ext/psych/psych_to_ruby.c: ditto ++ * test/psych/helper.rb: ditto ++ * test/psych/test_safe_load.rb: tests for restricted subset. ++ * test/psych/test_scalar_scanner.rb: ditto ++ * test/psych/visitors/test_to_ruby.rb: ditto ++ * test/psych/visitors/test_yaml_tree.rb: ditto + + Tue May 14 20:21:41 2013 Eric Hodel + +diff --git a/ext/psych/lib/psych.rb b/ext/psych/lib/psych.rb +index 66a0641f39d8..711b3c1377dc 100644 +--- a/ext/psych/lib/psych.rb ++++ b/ext/psych/lib/psych.rb +@@ -124,6 +124,55 @@ def self.load yaml, filename = nil + result ? result.to_ruby : result + end + ++ ### ++ # Safely load the yaml string in +yaml+. By default, only the following ++ # classes are allowed to be deserialized: ++ # ++ # * TrueClass ++ # * FalseClass ++ # * NilClass ++ # * Numeric ++ # * String ++ # * Array ++ # * Hash ++ # ++ # Recursive data structures are not allowed by default. Arbitrary classes ++ # can be allowed by adding those classes to the +whitelist+. They are ++ # additive. For example, to allow Date deserialization: ++ # ++ # Psych.safe_load(yaml, [Date]) ++ # ++ # Now the Date class can be loaded in addition to the classes listed above. ++ # ++ # Aliases can be explicitly allowed by changing the +aliases+ parameter. ++ # For example: ++ # ++ # x = [] ++ # x << x ++ # yaml = Psych.dump x ++ # Psych.safe_load yaml # => raises an exception ++ # Psych.safe_load yaml, [], [], true # => loads the aliases ++ # ++ # A Psych::DisallowedClass exception will be raised if the yaml contains a ++ # class that isn't in the whitelist. ++ # ++ # A Psych::BadAlias exception will be raised if the yaml contains aliases ++ # but the +aliases+ parameter is set to false. ++ def self.safe_load yaml, whitelist_classes = [], whitelist_symbols = [], aliases = false, filename = nil ++ result = parse(yaml, filename) ++ return unless result ++ ++ class_loader = ClassLoader::Restricted.new(whitelist_classes.map(&:to_s), ++ whitelist_symbols.map(&:to_s)) ++ scanner = ScalarScanner.new class_loader ++ if aliases ++ visitor = Visitors::ToRuby.new scanner, class_loader ++ else ++ visitor = Visitors::NoAliasRuby.new scanner, class_loader ++ end ++ visitor.accept result ++ end ++ + ### + # Parse a YAML string in +yaml+. Returns the first object of a YAML AST. + # +filename+ is used in the exception message if a Psych::SyntaxError is +@@ -234,7 +283,7 @@ def self.dump o, io = nil, options = {} + io = nil + end + +- visitor = Psych::Visitors::YAMLTree.new options ++ visitor = Psych::Visitors::YAMLTree.create options + visitor << o + visitor.tree.yaml io, options + end +@@ -246,7 +295,7 @@ def self.dump o, io = nil, options = {} + # + # Psych.dump_stream("foo\n ", {}) # => "--- ! \"foo\\n \"\n--- {}\n" + def self.dump_stream *objects +- visitor = Psych::Visitors::YAMLTree.new {} ++ visitor = Psych::Visitors::YAMLTree.create({}) + objects.each do |o| + visitor << o + end +@@ -256,7 +305,7 @@ def self.dump_stream *objects + ### + # Dump Ruby object +o+ to a JSON string. + def self.to_json o +- visitor = Psych::Visitors::JSONTree.new ++ visitor = Psych::Visitors::JSONTree.create + visitor << o + visitor.tree.yaml + end +@@ -314,7 +363,7 @@ def self.remove_type type_tag + @load_tags = {} + @dump_tags = {} + def self.add_tag tag, klass +- @load_tags[tag] = klass ++ @load_tags[tag] = klass.name + @dump_tags[klass] = tag + end + +diff --git a/ext/psych/lib/psych/class_loader.rb b/ext/psych/lib/psych/class_loader.rb +new file mode 100644 +index 000000000000..46c6b9362790 +--- /dev/null ++++ b/ext/psych/lib/psych/class_loader.rb +@@ -0,0 +1,101 @@ ++require 'psych/omap' ++require 'psych/set' ++ ++module Psych ++ class ClassLoader # :nodoc: ++ BIG_DECIMAL = 'BigDecimal' ++ COMPLEX = 'Complex' ++ DATE = 'Date' ++ DATE_TIME = 'DateTime' ++ EXCEPTION = 'Exception' ++ OBJECT = 'Object' ++ PSYCH_OMAP = 'Psych::Omap' ++ PSYCH_SET = 'Psych::Set' ++ RANGE = 'Range' ++ RATIONAL = 'Rational' ++ REGEXP = 'Regexp' ++ STRUCT = 'Struct' ++ SYMBOL = 'Symbol' ++ ++ def initialize ++ @cache = CACHE.dup ++ end ++ ++ def load klassname ++ return nil if !klassname || klassname.empty? ++ ++ find klassname ++ end ++ ++ def symbolize sym ++ symbol ++ sym.to_sym ++ end ++ ++ constants.each do |const| ++ konst = const_get const ++ define_method(const.to_s.downcase) do ++ load konst ++ end ++ end ++ ++ private ++ ++ def find klassname ++ @cache[klassname] ||= resolve(klassname) ++ end ++ ++ def resolve klassname ++ name = klassname ++ retried = false ++ ++ begin ++ path2class(name) ++ rescue ArgumentError, NameError => ex ++ unless retried ++ name = "Struct::#{name}" ++ retried = ex ++ retry ++ end ++ raise retried ++ end ++ end ++ ++ CACHE = Hash[constants.map { |const| ++ val = const_get const ++ begin ++ [val, ::Object.const_get(val)] ++ rescue ++ nil ++ end ++ }.compact] ++ ++ class Restricted < ClassLoader ++ def initialize classes, symbols ++ @classes = classes ++ @symbols = symbols ++ super() ++ end ++ ++ def symbolize sym ++ return super if @symbols.empty? ++ ++ if @symbols.include? sym ++ super ++ else ++ raise DisallowedClass, 'Symbol' ++ end ++ end ++ ++ private ++ ++ def find klassname ++ if @classes.include? klassname ++ super ++ else ++ raise DisallowedClass, klassname ++ end ++ end ++ end ++ end ++end +diff --git a/ext/psych/lib/psych/deprecated.rb b/ext/psych/lib/psych/deprecated.rb +index 1e42859b22fe..8c310b320738 100644 +--- a/ext/psych/lib/psych/deprecated.rb ++++ b/ext/psych/lib/psych/deprecated.rb +@@ -35,7 +35,8 @@ def self.detect_implicit thing + warn "#{caller[0]}: detect_implicit is deprecated" if $VERBOSE + return '' unless String === thing + return 'null' if '' == thing +- ScalarScanner.new.tokenize(thing).class.name.downcase ++ ss = ScalarScanner.new(ClassLoader.new) ++ ss.tokenize(thing).class.name.downcase + end + + def self.add_ruby_type type_tag, &block +diff --git a/ext/psych/lib/psych/exception.rb b/ext/psych/lib/psych/exception.rb +index d96c527cfba7..ce9d2caf3fb2 100644 +--- a/ext/psych/lib/psych/exception.rb ++++ b/ext/psych/lib/psych/exception.rb +@@ -4,4 +4,10 @@ class Exception < RuntimeError + + class BadAlias < Exception + end ++ ++ class DisallowedClass < Exception ++ def initialize klass_name ++ super "Tried to load unspecified class: #{klass_name}" ++ end ++ end + end +diff --git a/ext/psych/lib/psych/json/stream.rb b/ext/psych/lib/psych/json/stream.rb +index be1a0a8a8240..fe2a6e911650 100644 +--- a/ext/psych/lib/psych/json/stream.rb ++++ b/ext/psych/lib/psych/json/stream.rb +@@ -6,6 +6,7 @@ module JSON + class Stream < Psych::Visitors::JSONTree + include Psych::JSON::RubyEvents + include Psych::Streaming ++ extend Psych::Streaming::ClassMethods + + class Emitter < Psych::Stream::Emitter # :nodoc: + include Psych::JSON::YAMLEvents +diff --git a/ext/psych/lib/psych/nodes/node.rb b/ext/psych/lib/psych/nodes/node.rb +index 0cefe44e446d..83233a61fdd3 100644 +--- a/ext/psych/lib/psych/nodes/node.rb ++++ b/ext/psych/lib/psych/nodes/node.rb +@@ -1,4 +1,6 @@ + require 'stringio' ++require 'psych/class_loader' ++require 'psych/scalar_scanner' + + module Psych + module Nodes +@@ -32,7 +34,7 @@ def each &block + # + # See also Psych::Visitors::ToRuby + def to_ruby +- Visitors::ToRuby.new.accept self ++ Visitors::ToRuby.create.accept(self) + end + alias :transform :to_ruby + +diff --git a/ext/psych/lib/psych/scalar_scanner.rb b/ext/psych/lib/psych/scalar_scanner.rb +index 8aa594e3337c..5935e26b288a 100644 +--- a/ext/psych/lib/psych/scalar_scanner.rb ++++ b/ext/psych/lib/psych/scalar_scanner.rb +@@ -19,10 +19,13 @@ class ScalarScanner + |[-+]?(?:0|[1-9][0-9_]*) (?# base 10) + |[-+]?0x[0-9a-fA-F_]+ (?# base 16))$/x + ++ attr_reader :class_loader ++ + # Create a new scanner +- def initialize ++ def initialize class_loader + @string_cache = {} + @symbol_cache = {} ++ @class_loader = class_loader + end + + # Tokenize +string+ returning the ruby object +@@ -63,7 +66,7 @@ def tokenize string + when /^\d{4}-(?:1[012]|0\d|\d)-(?:[12]\d|3[01]|0\d|\d)$/ + require 'date' + begin +- Date.strptime(string, '%Y-%m-%d') ++ class_loader.date.strptime(string, '%Y-%m-%d') + rescue ArgumentError + string + end +@@ -75,9 +78,9 @@ def tokenize string + Float::NAN + when /^:./ + if string =~ /^:(["'])(.*)\1/ +- @symbol_cache[string] = $2.sub(/^:/, '').to_sym ++ @symbol_cache[string] = class_loader.symbolize($2.sub(/^:/, '')) + else +- @symbol_cache[string] = string.sub(/^:/, '').to_sym ++ @symbol_cache[string] = class_loader.symbolize(string.sub(/^:/, '')) + end + when /^[-+]?[0-9][0-9_]*(:[0-5]?[0-9])+$/ + i = 0 +@@ -117,6 +120,8 @@ def parse_int string + ### + # Parse and return a Time from +string+ + def parse_time string ++ klass = class_loader.load 'Time' ++ + date, time = *(string.split(/[ tT]/, 2)) + (yy, m, dd) = date.split('-').map { |x| x.to_i } + md = time.match(/(\d+:\d+:\d+)(?:\.(\d*))?\s*(Z|[-+]\d+(:\d\d)?)?/) +@@ -124,10 +129,10 @@ def parse_time string + (hh, mm, ss) = md[1].split(':').map { |x| x.to_i } + us = (md[2] ? Rational("0.#{md[2]}") : 0) * 1000000 + +- time = Time.utc(yy, m, dd, hh, mm, ss, us) ++ time = klass.utc(yy, m, dd, hh, mm, ss, us) + + return time if 'Z' == md[3] +- return Time.at(time.to_i, us) unless md[3] ++ return klass.at(time.to_i, us) unless md[3] + + tz = md[3].match(/^([+\-]?\d{1,2})\:?(\d{1,2})?$/)[1..-1].compact.map { |digit| Integer(digit, 10) } + offset = tz.first * 3600 +@@ -138,7 +143,7 @@ def parse_time string + offset += ((tz[1] || 0) * 60) + end + +- Time.at((time - offset).to_i, us) ++ klass.at((time - offset).to_i, us) + end + end + end +diff --git a/ext/psych/lib/psych/stream.rb b/ext/psych/lib/psych/stream.rb +index 567c1bb790f9..88c4c4cb4e18 100644 +--- a/ext/psych/lib/psych/stream.rb ++++ b/ext/psych/lib/psych/stream.rb +@@ -32,5 +32,6 @@ def streaming? + end + + include Psych::Streaming ++ extend Psych::Streaming::ClassMethods + end + end +diff --git a/ext/psych/lib/psych/streaming.rb b/ext/psych/lib/psych/streaming.rb +index c6fa109d5a61..9d94eb549f26 100644 +--- a/ext/psych/lib/psych/streaming.rb ++++ b/ext/psych/lib/psych/streaming.rb +@@ -1,10 +1,15 @@ + module Psych + module Streaming +- ### +- # Create a new streaming emitter. Emitter will print to +io+. See +- # Psych::Stream for an example. +- def initialize io +- super({}, self.class.const_get(:Emitter).new(io)) ++ module ClassMethods ++ ### ++ # Create a new streaming emitter. Emitter will print to +io+. See ++ # Psych::Stream for an example. ++ def new io ++ emitter = const_get(:Emitter).new(io) ++ class_loader = ClassLoader.new ++ ss = ScalarScanner.new class_loader ++ super(emitter, ss, {}) ++ end + end + + ### +diff --git a/ext/psych/lib/psych/visitors/json_tree.rb b/ext/psych/lib/psych/visitors/json_tree.rb +index 0350dd1faae0..0127ac8aa8c1 100644 +--- a/ext/psych/lib/psych/visitors/json_tree.rb ++++ b/ext/psych/lib/psych/visitors/json_tree.rb +@@ -5,8 +5,11 @@ module Visitors + class JSONTree < YAMLTree + include Psych::JSON::RubyEvents + +- def initialize options = {}, emitter = Psych::JSON::TreeBuilder.new +- super ++ def self.create options = {} ++ emitter = Psych::JSON::TreeBuilder.new ++ class_loader = ClassLoader.new ++ ss = ScalarScanner.new class_loader ++ new(emitter, ss, options) + end + + def accept target +diff --git a/ext/psych/lib/psych/visitors/to_ruby.rb b/ext/psych/lib/psych/visitors/to_ruby.rb +index 75c7bc0c550a..f770bb80aa3a 100644 +--- a/ext/psych/lib/psych/visitors/to_ruby.rb ++++ b/ext/psych/lib/psych/visitors/to_ruby.rb +@@ -1,4 +1,5 @@ + require 'psych/scalar_scanner' ++require 'psych/class_loader' + require 'psych/exception' + + unless defined?(Regexp::NOENCODING) +@@ -10,11 +11,20 @@ module Visitors + ### + # This class walks a YAML AST, converting each node to ruby + class ToRuby < Psych::Visitors::Visitor +- def initialize ss = ScalarScanner.new ++ def self.create ++ class_loader = ClassLoader.new ++ scanner = ScalarScanner.new class_loader ++ new(scanner, class_loader) ++ end ++ ++ attr_reader :class_loader ++ ++ def initialize ss, class_loader + super() + @st = {} + @ss = ss + @domain_types = Psych.domain_types ++ @class_loader = class_loader + end + + def accept target +@@ -33,7 +43,7 @@ def accept target + end + + def deserialize o +- if klass = Psych.load_tags[o.tag] ++ if klass = resolve_class(Psych.load_tags[o.tag]) + instance = klass.allocate + + if instance.respond_to?(:init_with) +@@ -60,19 +70,23 @@ def deserialize o + end + when '!ruby/object:BigDecimal' + require 'bigdecimal' +- BigDecimal._load o.value ++ class_loader.big_decimal._load o.value + when "!ruby/object:DateTime" ++ class_loader.date_time + require 'date' + @ss.parse_time(o.value).to_datetime + when "!ruby/object:Complex" ++ class_loader.complex + Complex(o.value) + when "!ruby/object:Rational" ++ class_loader.rational + Rational(o.value) + when "!ruby/class", "!ruby/module" + resolve_class o.value + when "tag:yaml.org,2002:float", "!float" + Float(@ss.tokenize(o.value)) + when "!ruby/regexp" ++ klass = class_loader.regexp + o.value =~ /^\/(.*)\/([mixn]*)$/ + source = $1 + options = 0 +@@ -86,15 +100,16 @@ def deserialize o + else lang = option + end + end +- Regexp.new(*[source, options, lang].compact) ++ klass.new(*[source, options, lang].compact) + when "!ruby/range" ++ klass = class_loader.range + args = o.value.split(/([.]{2,3})/, 2).map { |s| + accept Nodes::Scalar.new(s) + } + args.push(args.delete_at(1) == '...') +- Range.new(*args) ++ klass.new(*args) + when /^!ruby\/sym(bol)?:?(.*)?$/ +- o.value.to_sym ++ class_loader.symbolize o.value + else + @ss.tokenize o.value + end +@@ -106,7 +121,7 @@ def visit_Psych_Nodes_Scalar o + end + + def visit_Psych_Nodes_Sequence o +- if klass = Psych.load_tags[o.tag] ++ if klass = resolve_class(Psych.load_tags[o.tag]) + instance = klass.allocate + + if instance.respond_to?(:init_with) +@@ -138,22 +153,24 @@ def visit_Psych_Nodes_Sequence o + end + + def visit_Psych_Nodes_Mapping o +- return revive(Psych.load_tags[o.tag], o) if Psych.load_tags[o.tag] ++ if Psych.load_tags[o.tag] ++ return revive(resolve_class(Psych.load_tags[o.tag]), o) ++ end + return revive_hash({}, o) unless o.tag + + case o.tag + when /^!ruby\/struct:?(.*)?$/ +- klass = resolve_class($1) ++ klass = resolve_class($1) if $1 + + if klass + s = register(o, klass.allocate) + + members = {} +- struct_members = s.members.map { |x| x.to_sym } ++ struct_members = s.members.map { |x| class_loader.symbolize x } + o.children.each_slice(2) do |k,v| + member = accept(k) + value = accept(v) +- if struct_members.include?(member.to_sym) ++ if struct_members.include?(class_loader.symbolize(member)) + s.send("#{member}=", value) + else + members[member.to_s.sub(/^@/, '')] = value +@@ -161,22 +178,27 @@ def visit_Psych_Nodes_Mapping o + end + init_with(s, members, o) + else ++ klass = class_loader.struct + members = o.children.map { |c| accept c } + h = Hash[*members] +- Struct.new(*h.map { |k,v| k.to_sym }).new(*h.map { |k,v| v }) ++ klass.new(*h.map { |k,v| ++ class_loader.symbolize k ++ }).new(*h.map { |k,v| v }) + end + + when /^!ruby\/object:?(.*)?$/ + name = $1 || 'Object' + + if name == 'Complex' ++ class_loader.complex + h = Hash[*o.children.map { |c| accept c }] + register o, Complex(h['real'], h['image']) + elsif name == 'Rational' ++ class_loader.rational + h = Hash[*o.children.map { |c| accept c }] + register o, Rational(h['numerator'], h['denominator']) + else +- obj = revive((resolve_class(name) || Object), o) ++ obj = revive((resolve_class(name) || class_loader.object), o) + obj + end + +@@ -204,18 +226,19 @@ def visit_Psych_Nodes_Mapping o + list + + when '!ruby/range' ++ klass = class_loader.range + h = Hash[*o.children.map { |c| accept c }] +- register o, Range.new(h['begin'], h['end'], h['excl']) ++ register o, klass.new(h['begin'], h['end'], h['excl']) + + when /^!ruby\/exception:?(.*)?$/ + h = Hash[*o.children.map { |c| accept c }] + +- e = build_exception((resolve_class($1) || Exception), ++ e = build_exception((resolve_class($1) || class_loader.exception), + h.delete('message')) + init_with(e, h, o) + + when '!set', 'tag:yaml.org,2002:set' +- set = Psych::Set.new ++ set = class_loader.psych_set.new + @st[o.anchor] = set if o.anchor + o.children.each_slice(2) do |k,v| + set[accept(k)] = accept(v) +@@ -226,7 +249,7 @@ def visit_Psych_Nodes_Mapping o + revive_hash resolve_class($1).new, o + + when '!omap', 'tag:yaml.org,2002:omap' +- map = register(o, Psych::Omap.new) ++ map = register(o, class_loader.psych_omap.new) + o.children.each_slice(2) do |l,r| + map[accept(l)] = accept r + end +@@ -326,21 +349,13 @@ def init_with o, h, node + + # Convert +klassname+ to a Class + def resolve_class klassname +- return nil unless klassname and not klassname.empty? +- +- name = klassname +- retried = false +- +- begin +- path2class(name) +- rescue ArgumentError, NameError => ex +- unless retried +- name = "Struct::#{name}" +- retried = ex +- retry +- end +- raise retried +- end ++ class_loader.load klassname ++ end ++ end ++ ++ class NoAliasRuby < ToRuby ++ def visit_Psych_Nodes_Alias o ++ raise BadAlias, "Unknown alias: #{o.anchor}" + end + end + end +diff --git a/ext/psych/lib/psych/visitors/yaml_tree.rb b/ext/psych/lib/psych/visitors/yaml_tree.rb +index 96640e026719..ddd745b34a9c 100644 +--- a/ext/psych/lib/psych/visitors/yaml_tree.rb ++++ b/ext/psych/lib/psych/visitors/yaml_tree.rb +@@ -1,3 +1,7 @@ ++require 'psych/tree_builder' ++require 'psych/scalar_scanner' ++require 'psych/class_loader' ++ + module Psych + module Visitors + ### +@@ -36,7 +40,14 @@ def node_for target + alias :finished? :finished + alias :started? :started + +- def initialize options = {}, emitter = TreeBuilder.new, ss = ScalarScanner.new ++ def self.create options = {}, emitter = nil ++ emitter ||= TreeBuilder.new ++ class_loader = ClassLoader.new ++ ss = ScalarScanner.new class_loader ++ new(emitter, ss, options) ++ end ++ ++ def initialize emitter, ss, options + super() + @started = false + @finished = false +diff --git a/ext/psych/psych_to_ruby.c b/ext/psych/psych_to_ruby.c +index ed5245e12e7a..3cc87a965ec1 100644 +--- a/ext/psych/psych_to_ruby.c ++++ b/ext/psych/psych_to_ruby.c +@@ -31,11 +31,13 @@ static VALUE path2class(VALUE self, VALUE path) + void Init_psych_to_ruby(void) + { + VALUE psych = rb_define_module("Psych"); ++ VALUE class_loader = rb_define_class_under(psych, "ClassLoader", rb_cObject); ++ + VALUE visitors = rb_define_module_under(psych, "Visitors"); + VALUE visitor = rb_define_class_under(visitors, "Visitor", rb_cObject); + cPsychVisitorsToRuby = rb_define_class_under(visitors, "ToRuby", visitor); + + rb_define_private_method(cPsychVisitorsToRuby, "build_exception", build_exception, 2); +- rb_define_private_method(cPsychVisitorsToRuby, "path2class", path2class, 1); ++ rb_define_private_method(class_loader, "path2class", path2class, 1); + } + /* vim: set noet sws=4 sw=4: */ +diff --git a/test/psych/helper.rb b/test/psych/helper.rb +index 77ab0bb9d71c..f9b73cf5b588 100644 +--- a/test/psych/helper.rb ++++ b/test/psych/helper.rb +@@ -31,7 +31,7 @@ def assert_parse_only( obj, yaml ) + end + + def assert_cycle( obj ) +- v = Visitors::YAMLTree.new ++ v = Visitors::YAMLTree.create + v << obj + assert_equal(obj, Psych.load(v.tree.yaml)) + assert_equal( obj, Psych::load(Psych.dump(obj))) +diff --git a/test/psych/test_safe_load.rb b/test/psych/test_safe_load.rb +new file mode 100644 +index 000000000000..dd299c0ebf40 +--- /dev/null ++++ b/test/psych/test_safe_load.rb +@@ -0,0 +1,97 @@ ++require 'psych/helper' ++ ++module Psych ++ class TestSafeLoad < TestCase ++ class Foo; end ++ ++ [1, 2.2, {}, [], "foo"].each do |obj| ++ define_method(:"test_basic_#{obj.class}") do ++ assert_safe_cycle obj ++ end ++ end ++ ++ def test_no_recursion ++ x = [] ++ x << x ++ assert_raises(Psych::BadAlias) do ++ Psych.safe_load Psych.dump(x) ++ end ++ end ++ ++ def test_explicit_recursion ++ x = [] ++ x << x ++ assert_equal(x, Psych.safe_load(Psych.dump(x), [], [], true)) ++ end ++ ++ def test_symbol_whitelist ++ yml = Psych.dump :foo ++ assert_raises(Psych::DisallowedClass) do ++ Psych.safe_load yml ++ end ++ assert_equal(:foo, Psych.safe_load(yml, [Symbol], [:foo])) ++ end ++ ++ def test_symbol ++ assert_raises(Psych::DisallowedClass) do ++ assert_safe_cycle :foo ++ end ++ assert_raises(Psych::DisallowedClass) do ++ Psych.safe_load '--- !ruby/symbol foo', [] ++ end ++ assert_safe_cycle :foo, [Symbol] ++ assert_safe_cycle :foo, %w{ Symbol } ++ assert_equal :foo, Psych.safe_load('--- !ruby/symbol foo', [Symbol]) ++ end ++ ++ def test_foo ++ assert_raises(Psych::DisallowedClass) do ++ Psych.safe_load '--- !ruby/object:Foo {}', [Foo] ++ end ++ assert_raises(Psych::DisallowedClass) do ++ assert_safe_cycle Foo.new ++ end ++ assert_kind_of(Foo, Psych.safe_load(Psych.dump(Foo.new), [Foo])) ++ end ++ ++ X = Struct.new(:x) ++ def test_struct_depends_on_sym ++ assert_safe_cycle(X.new, [X, Symbol]) ++ assert_raises(Psych::DisallowedClass) do ++ cycle X.new, [X] ++ end ++ end ++ ++ def test_anon_struct ++ assert Psych.safe_load(<<-eoyml, [Struct, Symbol]) ++--- !ruby/struct ++ foo: bar ++ eoyml ++ ++ assert_raises(Psych::DisallowedClass) do ++ Psych.safe_load(<<-eoyml, [Struct]) ++--- !ruby/struct ++ foo: bar ++ eoyml ++ end ++ ++ assert_raises(Psych::DisallowedClass) do ++ Psych.safe_load(<<-eoyml, [Symbol]) ++--- !ruby/struct ++ foo: bar ++ eoyml ++ end ++ end ++ ++ private ++ ++ def cycle object, whitelist = [] ++ Psych.safe_load(Psych.dump(object), whitelist) ++ end ++ ++ def assert_safe_cycle object, whitelist = [] ++ other = cycle object, whitelist ++ assert_equal object, other ++ end ++ end ++end +diff --git a/test/psych/test_scalar_scanner.rb b/test/psych/test_scalar_scanner.rb +index a7bf17c912b6..e8e423cb053d 100644 +--- a/test/psych/test_scalar_scanner.rb ++++ b/test/psych/test_scalar_scanner.rb +@@ -7,7 +7,7 @@ class TestScalarScanner < TestCase + + def setup + super +- @ss = Psych::ScalarScanner.new ++ @ss = Psych::ScalarScanner.new ClassLoader.new + end + + def test_scan_time +diff --git a/test/psych/visitors/test_to_ruby.rb b/test/psych/visitors/test_to_ruby.rb +index 022cc2d2d4ea..c13d980468d4 100644 +--- a/test/psych/visitors/test_to_ruby.rb ++++ b/test/psych/visitors/test_to_ruby.rb +@@ -6,7 +6,7 @@ module Visitors + class TestToRuby < TestCase + def setup + super +- @visitor = ToRuby.new ++ @visitor = ToRuby.create + end + + def test_object +@@ -88,7 +88,7 @@ def test_anon_struct + end + + def test_exception +- exc = Exception.new 'hello' ++ exc = ::Exception.new 'hello' + + mapping = Nodes::Mapping.new nil, '!ruby/exception' + mapping.children << Nodes::Scalar.new('message') +diff --git a/test/psych/visitors/test_yaml_tree.rb b/test/psych/visitors/test_yaml_tree.rb +index 496cdd05cc34..40702bce796f 100644 +--- a/test/psych/visitors/test_yaml_tree.rb ++++ b/test/psych/visitors/test_yaml_tree.rb +@@ -5,7 +5,7 @@ module Visitors + class TestYAMLTree < TestCase + def setup + super +- @v = Visitors::YAMLTree.new ++ @v = Visitors::YAMLTree.create + end + + def test_tree_can_be_called_twice +@@ -18,7 +18,7 @@ def test_tree_can_be_called_twice + def test_yaml_tree_can_take_an_emitter + io = StringIO.new + e = Psych::Emitter.new io +- v = Visitors::YAMLTree.new({}, e) ++ v = Visitors::YAMLTree.create({}, e) + v.start + v << "hello world" + v.finish diff --git a/SOURCES/ruby-2.1.0-there-should-be-only-one-exception.patch b/SOURCES/ruby-2.1.0-there-should-be-only-one-exception.patch new file mode 100644 index 0000000..e9b6995 --- /dev/null +++ b/SOURCES/ruby-2.1.0-there-should-be-only-one-exception.patch @@ -0,0 +1,93 @@ +From 476a62fbbec0c8b7dafb74827447cfb4ebd7dd06 Mon Sep 17 00:00:00 2001 +From: tenderlove +Date: Fri, 5 Apr 2013 17:55:53 +0000 +Subject: [PATCH] * ext/psych/lib/psych/exception.rb: there should be only one + exception base class. Fixes tenderlove/psych #125 * ext/psych/lib/psych.rb: + require the correct exception class * ext/psych/lib/psych/syntax_error.rb: + ditto * ext/psych/lib/psych/visitors/to_ruby.rb: ditto + +git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@40139 b2dd03c8-39d4-4d8f-98ff-823fe69b080e +--- + ChangeLog | 8 ++++++++ + ext/psych/lib/psych.rb | 6 ------ + ext/psych/lib/psych/exception.rb | 7 +++++++ + ext/psych/lib/psych/syntax_error.rb | 7 +++---- + ext/psych/lib/psych/visitors/to_ruby.rb | 1 + + 5 files changed, 19 insertions(+), 10 deletions(-) + create mode 100644 ext/psych/lib/psych/exception.rb + +diff --git a/ChangeLog b/ChangeLog +index f0dba22e9c27..304ecaba2854 100644 +--- a/ChangeLog ++++ b/ChangeLog +@@ -3666,6 +3666,14 @@ + * gc.c: Improve accuracy of objspace_live_num() and + allocated/freed counters. patched by tmm1(Aman Gupta). + [Bug #8092] [ruby-core:53392] ++ ++Sat Apr 6 02:54:08 2013 Aaron Patterson ++ ++ * ext/psych/lib/psych/exception.rb: there should be only one exception ++ base class. Fixes tenderlove/psych #125 ++ * ext/psych/lib/psych.rb: require the correct exception class ++ * ext/psych/lib/psych/syntax_error.rb: ditto ++ * ext/psych/lib/psych/visitors/to_ruby.rb: ditto + + Fri Apr 5 00:54:08 2013 NARUSE, Yui + +diff --git a/ext/psych/lib/psych.rb b/ext/psych/lib/psych.rb +index 3ca611748473..7d7d2bfb00f4 100644 +--- a/ext/psych/lib/psych.rb ++++ b/ext/psych/lib/psych.rb +@@ -100,12 +100,6 @@ module Psych + # The version of libyaml Psych is using + LIBYAML_VERSION = Psych.libyaml_version.join '.' + +- class Exception < RuntimeError +- end +- +- class BadAlias < Exception +- end +- + ### + # Load +yaml+ in to a Ruby data structure. If multiple documents are + # provided, the object contained in the first document will be returned. +diff --git a/ext/psych/lib/psych/exception.rb b/ext/psych/lib/psych/exception.rb +new file mode 100644 +index 000000000000..d96c527cfba7 +--- /dev/null ++++ b/ext/psych/lib/psych/exception.rb +@@ -0,0 +1,7 @@ ++module Psych ++ class Exception < RuntimeError ++ end ++ ++ class BadAlias < Exception ++ end ++end +diff --git a/ext/psych/lib/psych/syntax_error.rb b/ext/psych/lib/psych/syntax_error.rb +index f972256f9e37..e200ef006067 100644 +--- a/ext/psych/lib/psych/syntax_error.rb ++++ b/ext/psych/lib/psych/syntax_error.rb +@@ -1,8 +1,7 @@ +-module Psych +- class Error < RuntimeError +- end ++require 'psych/exception' + +- class SyntaxError < Error ++module Psych ++ class SyntaxError < Psych::Exception + attr_reader :file, :line, :column, :offset, :problem, :context + + def initialize file, line, col, offset, problem, context +diff --git a/ext/psych/lib/psych/visitors/to_ruby.rb b/ext/psych/lib/psych/visitors/to_ruby.rb +index b59bc3883070..75c7bc0c550a 100644 +--- a/ext/psych/lib/psych/visitors/to_ruby.rb ++++ b/ext/psych/lib/psych/visitors/to_ruby.rb +@@ -1,4 +1,5 @@ + require 'psych/scalar_scanner' ++require 'psych/exception' + + unless defined?(Regexp::NOENCODING) + Regexp::NOENCODING = 32 diff --git a/SOURCES/ruby-2.2.8-Buffer-underrun-vulnerability-in-Kernel.sprintf.patch b/SOURCES/ruby-2.2.8-Buffer-underrun-vulnerability-in-Kernel.sprintf.patch new file mode 100644 index 0000000..f095685 --- /dev/null +++ b/SOURCES/ruby-2.2.8-Buffer-underrun-vulnerability-in-Kernel.sprintf.patch @@ -0,0 +1,184 @@ +From 4fdfb28e7d2e3eefc0df1e1d034fbfc932c0d2a1 Mon Sep 17 00:00:00 2001 +From: usa +Date: Thu, 14 Sep 2017 11:35:52 +0000 +Subject: [PATCH] merge revision(s) 58453,58454: [Backport #13499] + + Fix space flag when Inf/NaN and width==3 + + * sprintf.c (rb_str_format): while `"% 2f"` and `"% 4f"` result in + `" Inf"` and `" Inf"` respectively, `"% 3f"` results in + `"Inf"` (no space). + Refactor "%f" % Inf/NaN + + * sprintf.c (rb_str_format): as for non-finite float, calculate + the exact needed size with the space flag. + +git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_2_2@59901 b2dd03c8-39d4-4d8f-98ff-823fe69b080e +--- + ChangeLog | 12 ++++++++++ + sprintf.c | 37 +++++++++++++++----------------- + test/ruby/test_sprintf.rb | 52 ++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 81 insertions(+), 20 deletions(-) + +diff --git a/ChangeLog b/ChangeLog +index ef36ffbd1552..a4594f678f8c 100644 +--- a/ChangeLog ++++ b/ChangeLog +@@ -4,6 +4,18 @@ + protocol list. + The protocol list from OpenSSL is not null-terminated. + patched by Kazuki Yamaguchi [Bug #11810] [ruby-core:72082] ++ ++Thu Sep 14 20:33:52 2017 Nobuyoshi Nakada ++ ++ Fix space flag when Inf/NaN and width==3 ++ ++ * sprintf.c (rb_str_format): while "% 2f" and "% 4f" result in " Inf" ++ and " Inf" respectively, "% 3f" results in "Inf" (no space). ++ ++ Refactor "%f" % Inf/NaN ++ ++ * sprintf.c (rb_str_format): as for non-finite float, calculate the ++ exact needed size with the space flag. + + Sun Sep 10 10:10:05 2017 SHIBATA Hiroshi + +diff --git a/sprintf.c b/sprintf.c +index 70c7cceb14d6..db7499979dc3 100644 +--- a/sprintf.c ++++ b/sprintf.c +@@ -1025,6 +1025,8 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt) + fval = RFLOAT_VALUE(rb_Float(val)); + if (isnan(fval) || isinf(fval)) { + const char *expr; ++ int elen; ++ char sign = '\0'; + + if (isnan(fval)) { + expr = "NaN"; +@@ -1033,33 +1035,28 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt) + expr = "Inf"; + } + need = (int)strlen(expr); +- if ((!isnan(fval) && fval < 0.0) || (flags & FPLUS)) +- need++; ++ elen = need; ++ i = 0; ++ if (!isnan(fval) && fval < 0.0) ++ sign = '-'; ++ else if (flags & (FPLUS|FSPACE)) ++ sign = (flags & FPLUS) ? '+' : ' '; ++ if (sign) ++ ++need; + if ((flags & FWIDTH) && need < width) + need = width; + +- CHECK(need + 1); +- snprintf(&buf[blen], need + 1, "%*s", need, ""); ++ FILL(' ', need); + if (flags & FMINUS) { +- if (!isnan(fval) && fval < 0.0) +- buf[blen++] = '-'; +- else if (flags & FPLUS) +- buf[blen++] = '+'; +- else if (flags & FSPACE) +- blen++; +- memcpy(&buf[blen], expr, strlen(expr)); ++ if (sign) ++ buf[blen - need--] = sign; ++ memcpy(&buf[blen - need], expr, elen); + } + else { +- if (!isnan(fval) && fval < 0.0) +- buf[blen + need - strlen(expr) - 1] = '-'; +- else if (flags & FPLUS) +- buf[blen + need - strlen(expr) - 1] = '+'; +- else if ((flags & FSPACE) && need > width) +- blen++; +- memcpy(&buf[blen + need - strlen(expr)], expr, +- strlen(expr)); ++ if (sign) ++ buf[blen - elen - 1] = sign; ++ memcpy(&buf[blen - elen], expr, elen); + } +- blen += strlen(&buf[blen]); + break; + } + +diff --git a/test/ruby/test_sprintf.rb b/test/ruby/test_sprintf.rb +index 3fd4736a54b0..ab3037a8f267 100644 +--- a/test/ruby/test_sprintf.rb ++++ b/test/ruby/test_sprintf.rb +@@ -84,6 +84,18 @@ def test_nan + assert_equal("NaN", sprintf("%-f", nan)) + assert_equal("+NaN", sprintf("%+f", nan)) + ++ assert_equal("NaN", sprintf("%3f", nan)) ++ assert_equal("NaN", sprintf("%-3f", nan)) ++ assert_equal("+NaN", sprintf("%+3f", nan)) ++ ++ assert_equal(" NaN", sprintf("% 3f", nan)) ++ assert_equal(" NaN", sprintf("%- 3f", nan)) ++ assert_equal("+NaN", sprintf("%+ 3f", nan)) ++ ++ assert_equal(" NaN", sprintf("% 03f", nan)) ++ assert_equal(" NaN", sprintf("%- 03f", nan)) ++ assert_equal("+NaN", sprintf("%+ 03f", nan)) ++ + assert_equal(" NaN", sprintf("%8f", nan)) + assert_equal("NaN ", sprintf("%-8f", nan)) + assert_equal(" +NaN", sprintf("%+8f", nan)) +@@ -107,6 +119,26 @@ def test_inf + assert_equal("Inf", sprintf("%-f", inf)) + assert_equal("+Inf", sprintf("%+f", inf)) + ++ assert_equal(" Inf", sprintf("% f", inf)) ++ assert_equal(" Inf", sprintf("%- f", inf)) ++ assert_equal("+Inf", sprintf("%+ f", inf)) ++ ++ assert_equal(" Inf", sprintf("% 0f", inf)) ++ assert_equal(" Inf", sprintf("%- 0f", inf)) ++ assert_equal("+Inf", sprintf("%+ 0f", inf)) ++ ++ assert_equal("Inf", sprintf("%3f", inf)) ++ assert_equal("Inf", sprintf("%-3f", inf)) ++ assert_equal("+Inf", sprintf("%+3f", inf)) ++ ++ assert_equal(" Inf", sprintf("% 3f", inf)) ++ assert_equal(" Inf", sprintf("%- 3f", inf)) ++ assert_equal("+Inf", sprintf("%+ 3f", inf)) ++ ++ assert_equal(" Inf", sprintf("% 03f", inf)) ++ assert_equal(" Inf", sprintf("%- 03f", inf)) ++ assert_equal("+Inf", sprintf("%+ 03f", inf)) ++ + assert_equal(" Inf", sprintf("%8f", inf)) + assert_equal("Inf ", sprintf("%-8f", inf)) + assert_equal(" +Inf", sprintf("%+8f", inf)) +@@ -127,6 +159,26 @@ def test_inf + assert_equal("-Inf", sprintf("%-f", -inf)) + assert_equal("-Inf", sprintf("%+f", -inf)) + ++ assert_equal("-Inf", sprintf("% f", -inf)) ++ assert_equal("-Inf", sprintf("%- f", -inf)) ++ assert_equal("-Inf", sprintf("%+ f", -inf)) ++ ++ assert_equal("-Inf", sprintf("% 0f", -inf)) ++ assert_equal("-Inf", sprintf("%- 0f", -inf)) ++ assert_equal("-Inf", sprintf("%+ 0f", -inf)) ++ ++ assert_equal("-Inf", sprintf("%4f", -inf)) ++ assert_equal("-Inf", sprintf("%-4f", -inf)) ++ assert_equal("-Inf", sprintf("%+4f", -inf)) ++ ++ assert_equal("-Inf", sprintf("% 4f", -inf)) ++ assert_equal("-Inf", sprintf("%- 4f", -inf)) ++ assert_equal("-Inf", sprintf("%+ 4f", -inf)) ++ ++ assert_equal("-Inf", sprintf("% 04f", -inf)) ++ assert_equal("-Inf", sprintf("%- 04f", -inf)) ++ assert_equal("-Inf", sprintf("%+ 04f", -inf)) ++ + assert_equal(" -Inf", sprintf("%8f", -inf)) + assert_equal("-Inf ", sprintf("%-8f", -inf)) + assert_equal(" -Inf", sprintf("%+8f", -inf)) diff --git a/SOURCES/ruby-2.2.8-Fix-arbitrary-heap-exposure-during-a-JSON.generate-call.patch b/SOURCES/ruby-2.2.8-Fix-arbitrary-heap-exposure-during-a-JSON.generate-call.patch new file mode 100644 index 0000000..564c538 --- /dev/null +++ b/SOURCES/ruby-2.2.8-Fix-arbitrary-heap-exposure-during-a-JSON.generate-call.patch @@ -0,0 +1,97 @@ +From d629ce0baa47ce800a26b451215dbeb20b3fb05c Mon Sep 17 00:00:00 2001 +From: usa +Date: Thu, 14 Sep 2017 11:44:37 +0000 +Subject: [PATCH] * ext/json: bump to version 1.8.1.1. [Backport #13853] + +git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_2_2@59904 b2dd03c8-39d4-4d8f-98ff-823fe69b080e +--- + ChangeLog | 4 ++++ + ext/json/generator/generator.c | 12 ++++++------ + ext/json/generator/generator.h | 1 - + 3 files changed, 10 insertions(+), 7 deletions(-) + +diff --git a/ChangeLog b/ChangeLog +index 6288f67500fd..65f2d6bc08ac 100644 +--- a/ChangeLog ++++ b/ChangeLog +@@ -4,6 +4,10 @@ + protocol list. + The protocol list from OpenSSL is not null-terminated. + patched by Kazuki Yamaguchi [Bug #11810] [ruby-core:72082] ++ ++Thu Sep 14 20:44:26 2017 SHIBATA Hiroshi ++ ++ * ext/json: bump to version 1.8.1.1. [Backport #13853] + + Thu Sep 14 20:36:54 2017 Yusuke Endoh + +diff --git a/ext/json/generator/generator.c b/ext/json/generator/generator.c +index f56ac09cd286..ae0e73fcbce3 100644 +--- a/ext/json/generator/generator.c ++++ b/ext/json/generator/generator.c +@@ -290,7 +290,7 @@ static char *fstrndup(const char *ptr, unsigned long len) { + char *result; + if (len <= 0) return NULL; + result = ALLOC_N(char, len); +- memccpy(result, ptr, 0, len); ++ memcpy(result, ptr, len); + return result; + } + +@@ -1025,7 +1025,7 @@ static VALUE cState_indent_set(VALUE self, VALUE indent) + } + } else { + if (state->indent) ruby_xfree(state->indent); +- state->indent = strdup(RSTRING_PTR(indent)); ++ state->indent = fstrndup(RSTRING_PTR(indent), len); + state->indent_len = len; + } + return Qnil; +@@ -1063,7 +1063,7 @@ static VALUE cState_space_set(VALUE self, VALUE space) + } + } else { + if (state->space) ruby_xfree(state->space); +- state->space = strdup(RSTRING_PTR(space)); ++ state->space = fstrndup(RSTRING_PTR(space), len); + state->space_len = len; + } + return Qnil; +@@ -1099,7 +1099,7 @@ static VALUE cState_space_before_set(VALUE self, VALUE space_before) + } + } else { + if (state->space_before) ruby_xfree(state->space_before); +- state->space_before = strdup(RSTRING_PTR(space_before)); ++ state->space_before = fstrndup(RSTRING_PTR(space_before), len); + state->space_before_len = len; + } + return Qnil; +@@ -1136,7 +1136,7 @@ static VALUE cState_object_nl_set(VALUE self, VALUE object_nl) + } + } else { + if (state->object_nl) ruby_xfree(state->object_nl); +- state->object_nl = strdup(RSTRING_PTR(object_nl)); ++ state->object_nl = fstrndup(RSTRING_PTR(object_nl), len); + state->object_nl_len = len; + } + return Qnil; +@@ -1171,7 +1171,7 @@ static VALUE cState_array_nl_set(VALUE self, VALUE array_nl) + } + } else { + if (state->array_nl) ruby_xfree(state->array_nl); +- state->array_nl = strdup(RSTRING_PTR(array_nl)); ++ state->array_nl = fstrndup(RSTRING_PTR(array_nl), len); + state->array_nl_len = len; + } + return Qnil; +diff --git a/ext/json/generator/generator.h b/ext/json/generator/generator.h +index ddd1aa8a309b..395d71e9d34d 100644 +--- a/ext/json/generator/generator.h ++++ b/ext/json/generator/generator.h +@@ -1,7 +1,6 @@ + #ifndef _GENERATOR_H_ + #define _GENERATOR_H_ + +-#include + #include + #include + diff --git a/SOURCES/ruby-2.2.8-asn1-fix-out-of-bounds-read-in-decoding-constructed-objects.patch b/SOURCES/ruby-2.2.8-asn1-fix-out-of-bounds-read-in-decoding-constructed-objects.patch new file mode 100644 index 0000000..bb29b28 --- /dev/null +++ b/SOURCES/ruby-2.2.8-asn1-fix-out-of-bounds-read-in-decoding-constructed-objects.patch @@ -0,0 +1,118 @@ +From 5450329ad1778d72f117b68e5edb97ae1bf4d438 Mon Sep 17 00:00:00 2001 +From: usa +Date: Thu, 14 Sep 2017 11:41:59 +0000 +Subject: [PATCH] asn1: fix out-of-bounds read in decoding constructed objects + +* OpenSSL::ASN1.{decode,decode_all,traverse}: have a bug of + out-of-bounds read. int_ossl_asn1_decode0_cons() does not give the + correct available length to ossl_asn1_decode() when decoding the + inner components of a constructed object. This can cause + out-of-bounds read if a crafted input given. + +Reference: https://hackerone.com/reports/170316 +https://github.com/ruby/openssl/commit/1648afef33c1d97fb203c82291b8a61269e85d3b + + +git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_2_2@59903 b2dd03c8-39d4-4d8f-98ff-823fe69b080e +--- + ChangeLog | 13 +++++++++++++ + ext/openssl/ossl_asn1.c | 13 ++++++------- + test/openssl/test_asn1.rb | 23 +++++++++++++++++++++++ + 3 files changed, 42 insertions(+), 7 deletions(-) + +diff --git a/ChangeLog b/ChangeLog +index 7561c35eb705..6288f67500fd 100644 +--- a/ChangeLog ++++ b/ChangeLog +@@ -17,6 +17,19 @@ + Thu Sep 14 20:44:26 2017 SHIBATA Hiroshi + + * ext/json: bump to version 1.8.1.1. [Backport #13853] ++ ++Thu Sep 14 20:39:39 2017 Kazuki Yamaguchi ++ ++ asn1: fix out-of-bounds read in decoding constructed objects ++ ++ * OpenSSL::ASN1.{decode,decode_all,traverse}: have a bug of ++ out-of-bounds read. int_ossl_asn1_decode0_cons() does not give the ++ correct available length to ossl_asn1_decode() when decoding the ++ inner components of a constructed object. This can cause ++ out-of-bounds read if a crafted input given. ++ ++ Reference: https://hackerone.com/reports/170316 ++ https://github.com/ruby/openssl/commit/1648afef33c1d97fb203c82291b8a61269e85d3b + + Thu Sep 14 20:36:54 2017 Yusuke Endoh + +diff --git a/ext/openssl/ossl_asn1.c b/ext/openssl/ossl_asn1.c +index 6d564a312f35..719063c551e5 100644 +--- a/ext/openssl/ossl_asn1.c ++++ b/ext/openssl/ossl_asn1.c +@@ -871,19 +871,18 @@ int_ossl_asn1_decode0_cons(unsigned char **pp, long max_len, long length, + { + VALUE value, asn1data, ary; + int infinite; +- long off = *offset; ++ long available_len, off = *offset; + + infinite = (j == 0x21); + ary = rb_ary_new(); + +- while (length > 0 || infinite) { ++ available_len = infinite ? max_len : length; ++ while (available_len > 0) { + long inner_read = 0; +- value = ossl_asn1_decode0(pp, max_len, &off, depth + 1, yield, &inner_read); ++ value = ossl_asn1_decode0(pp, available_len, &off, depth + 1, yield, &inner_read); + *num_read += inner_read; +- max_len -= inner_read; ++ available_len -= inner_read; + rb_ary_push(ary, value); +- if (length > 0) +- length -= inner_read; + + if (infinite && + NUM2INT(ossl_asn1_get_tag(value)) == V_ASN1_EOC && +@@ -974,7 +973,7 @@ ossl_asn1_decode0(unsigned char **pp, long length, long *offset, int depth, + if(j & V_ASN1_CONSTRUCTED) { + *pp += hlen; + off += hlen; +- asn1data = int_ossl_asn1_decode0_cons(pp, length, len, &off, depth, yield, j, tag, tag_class, &inner_read); ++ asn1data = int_ossl_asn1_decode0_cons(pp, length - hlen, len, &off, depth, yield, j, tag, tag_class, &inner_read); + inner_read += hlen; + } + else { +diff --git a/test/openssl/test_asn1.rb b/test/openssl/test_asn1.rb +index 9fb5a551c66d..a6d7c2c14e00 100644 +--- a/test/openssl/test_asn1.rb ++++ b/test/openssl/test_asn1.rb +@@ -595,6 +595,29 @@ def test_recursive_octet_string_parse + assert_equal(false, asn1.value[3].infinite_length) + end + ++ def test_decode_constructed_overread ++ test = %w{ 31 06 31 02 30 02 05 00 } ++ # ^ <- invalid ++ raw = [test.join].pack("H*") ++ ret = [] ++ assert_raise(OpenSSL::ASN1::ASN1Error) { ++ OpenSSL::ASN1.traverse(raw) { |x| ret << x } ++ } ++ assert_equal 2, ret.size ++ assert_equal 17, ret[0][6] ++ assert_equal 17, ret[1][6] ++ ++ test = %w{ 31 80 30 03 00 00 } ++ # ^ <- invalid ++ raw = [test.join].pack("H*") ++ ret = [] ++ assert_raise(OpenSSL::ASN1::ASN1Error) { ++ OpenSSL::ASN1.traverse(raw) { |x| ret << x } ++ } ++ assert_equal 1, ret.size ++ assert_equal 17, ret[0][6] ++ end ++ + private + + def assert_universal(tag, asn1) diff --git a/SOURCES/ruby-2.2.8-lib-rubygems-fix-several-vulnerabilities-in-RubyGems.patch b/SOURCES/ruby-2.2.8-lib-rubygems-fix-several-vulnerabilities-in-RubyGems.patch new file mode 100644 index 0000000..86eb6ce --- /dev/null +++ b/SOURCES/ruby-2.2.8-lib-rubygems-fix-several-vulnerabilities-in-RubyGems.patch @@ -0,0 +1,393 @@ +From 97c6e3934c68e90592f6913f68861d0dbc49c6a4 Mon Sep 17 00:00:00 2001 +From: usa +Date: Sun, 10 Sep 2017 01:10:24 +0000 +Subject: [PATCH] * lib/rubygems: fix several vulnerabilities in RubyGems; bump + to version 2.4.5.3. [Backport #13842] + +git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_2_2@59805 b2dd03c8-39d4-4d8f-98ff-823fe69b080e +--- + ChangeLog | 4 + + lib/rubygems/commands/query_command.rb | 5 - + lib/rubygems/installer.rb | 7 ++ + lib/rubygems/remote_fetcher.rb | 2 +- + lib/rubygems/specification.rb | 12 ++- + lib/rubygems/text.rb | 15 ++++ + test/rubygems/test_gem_commands_query_command.rb | 80 +++++++++++++++++++++++ + test/rubygems/test_gem_installer.rb | 32 +++++++++ + test/rubygems/test_gem_remote_fetcher.rb | 15 ++++ + test/rubygems/test_gem_specification.rb | 32 ++++++++- + test/rubygems/test_gem_text.rb | 11 +++ + 11 files changed, 208 insertions(+), 7 deletions(-) + +diff --git a/ChangeLog b/ChangeLog +index 08bc53d050..ef36ffbd15 100644 +--- a/ChangeLog ++++ b/ChangeLog +@@ -4,6 +4,10 @@ + protocol list. + The protocol list from OpenSSL is not null-terminated. + patched by Kazuki Yamaguchi [Bug #11810] [ruby-core:72082] ++ ++Sun Sep 10 10:10:05 2017 SHIBATA Hiroshi ++ ++ * lib/rubygems: fix several vulnerabilities in RubyGems [Backport #13842] + + Thu Feb 25 19:49:31 2016 Nobuyoshi Nakada + +diff --git a/lib/rubygems/commands/query_command.rb b/lib/rubygems/commands/query_command.rb +index 432250e033..44364cfab2 100644 +--- a/lib/rubygems/commands/query_command.rb ++++ b/lib/rubygems/commands/query_command.rb +@@ -193,7 +193,7 @@ def output_versions output, versions + end + end + +- output << make_entry(matching_tuples, platforms) ++ output << clean_text(make_entry(matching_tuples, platforms)) + end + end + +@@ -311,7 +311,8 @@ def spec_platforms entry, platforms + end + + def spec_summary entry, spec +- entry << "\n\n" << format_text(spec.summary, 68, 4) ++ summary = truncate_text(spec.summary, "the summary for #{spec.full_name}") ++ entry << "\n\n" << format_text(summary, 68, 4) + end + + end +diff --git a/lib/rubygems/installer.rb b/lib/rubygems/installer.rb +index 10fc1a34a5..a27569fe2e 100644 +--- a/lib/rubygems/installer.rb ++++ b/lib/rubygems/installer.rb +@@ -596,6 +596,11 @@ def verify_gem_home(unpack = false) # :nodoc: + unpack or File.writable?(gem_home) + end + ++ def verify_spec_name ++ return if spec.name =~ Gem::Specification::VALID_NAME_PATTERN ++ raise Gem::InstallError, "#{spec} has an invalid name" ++ end ++ + ## + # Return the text for an application file. + +@@ -767,6 +772,8 @@ def pre_install_checks + + ensure_loadable_spec + ++ verify_spec_name ++ + Gem.ensure_gem_subdirectories gem_home + + return true if @force +diff --git a/lib/rubygems/remote_fetcher.rb b/lib/rubygems/remote_fetcher.rb +index b1f6dd17fc..2b9d61c0a1 100644 +--- a/lib/rubygems/remote_fetcher.rb ++++ b/lib/rubygems/remote_fetcher.rb +@@ -105,7 +105,7 @@ def api_endpoint(uri) + else + target = res.target.to_s.strip + +- if /\.#{Regexp.quote(host)}\z/ =~ target ++ if URI("http://" + target).host.end_with?(".#{host}") + return URI.parse "#{uri.scheme}://#{target}#{uri.path}" + end + +diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb +index ab1cd92270..faca837128 100644 +--- a/lib/rubygems/specification.rb ++++ b/lib/rubygems/specification.rb +@@ -107,6 +107,8 @@ class Gem::Specification < Gem::BasicSpecification + today = Time.now.utc + TODAY = Time.utc(today.year, today.month, today.day) + ++ VALID_NAME_PATTERN = /\A[a-zA-Z0-9\.\-\_]+\z/ # :nodoc: ++ + # :startdoc: + + ## +@@ -2377,9 +2379,15 @@ def validate packaging = true + end + end + +- unless String === name then ++ if !name.is_a?(String) then ++ raise Gem::InvalidSpecificationException, ++ "invalid value for attribute name: \"#{name.inspect}\" must be a string" ++ elsif name !~ /[a-zA-Z]/ then ++ raise Gem::InvalidSpecificationException, ++ "invalid value for attribute name: #{name.dump} must include at least one letter" ++ elsif name !~ VALID_NAME_PATTERN then + raise Gem::InvalidSpecificationException, +- "invalid value for attribute name: \"#{name.inspect}\"" ++ "invalid value for attribute name: #{name.dump} can only include letters, numbers, dashes, and underscores" + end + + if require_paths.empty? then +diff --git a/lib/rubygems/text.rb b/lib/rubygems/text.rb +index 5c9287ad2e..86a722ffc0 100644 +--- a/lib/rubygems/text.rb ++++ b/lib/rubygems/text.rb +@@ -5,13 +5,26 @@ + + module Gem::Text + ++ ## ++ # Remove any non-printable characters and make the text suitable for ++ # printing. ++ def clean_text(text) ++ text.gsub(/[\000-\b\v-\f\016-\037\177]/, ".".freeze) ++ end ++ ++ def truncate_text(text, description, max_length = 100_000) ++ raise ArgumentError, "max_length must be positive" unless max_length > 0 ++ return text if text.size <= max_length ++ "Truncating #{description} to #{max_length.to_s.reverse.gsub(/...(?=.)/,'\&,').reverse} characters:\n" + text[0, max_length] ++ end ++ + ## + # Wraps +text+ to +wrap+ characters and optionally indents by +indent+ + # characters + + def format_text(text, wrap, indent=0) + result = [] +- work = text.dup ++ work = clean_text(text) + + while work.length > wrap do + if work =~ /^(.{0,#{wrap}})[ \n]/ then +diff --git a/test/rubygems/test_gem_commands_query_command.rb b/test/rubygems/test_gem_commands_query_command.rb +index 43fa82571d..ccd2621874 100644 +--- a/test/rubygems/test_gem_commands_query_command.rb ++++ b/test/rubygems/test_gem_commands_query_command.rb +@@ -127,6 +127,86 @@ def test_execute_details + This is a lot of text. This is a lot of text. This is a lot of text. + This is a lot of text. + ++pl (1) ++ Platform: i386-linux ++ Author: A User ++ Homepage: http://example.com ++ ++ this is a summary ++ EOF ++ ++ assert_equal expected, @ui.output ++ assert_equal '', @ui.error ++ end ++ ++ def test_execute_details_cleans_text ++ spec_fetcher do |fetcher| ++ fetcher.spec 'a', 2 do |s| ++ s.summary = 'This is a lot of text. ' * 4 ++ s.authors = ["Abraham Lincoln \x01", "\x02 Hirohito"] ++ s.homepage = "http://a.example.com/\x03" ++ end ++ ++ fetcher.legacy_platform ++ end ++ ++ @cmd.handle_options %w[-r -d] ++ ++ use_ui @ui do ++ @cmd.execute ++ end ++ ++ expected = <<-EOF ++ ++*** REMOTE GEMS *** ++ ++a (2) ++ Authors: Abraham Lincoln ., . Hirohito ++ Homepage: http://a.example.com/. ++ ++ This is a lot of text. This is a lot of text. This is a lot of text. ++ This is a lot of text. ++ ++pl (1) ++ Platform: i386-linux ++ Author: A User ++ Homepage: http://example.com ++ ++ this is a summary ++ EOF ++ ++ assert_equal expected, @ui.output ++ assert_equal '', @ui.error ++ end ++ ++ def test_execute_details_truncates_summary ++ spec_fetcher do |fetcher| ++ fetcher.spec 'a', 2 do |s| ++ s.summary = 'This is a lot of text. ' * 10_000 ++ s.authors = ["Abraham Lincoln \x01", "\x02 Hirohito"] ++ s.homepage = "http://a.example.com/\x03" ++ end ++ ++ fetcher.legacy_platform ++ end ++ ++ @cmd.handle_options %w[-r -d] ++ ++ use_ui @ui do ++ @cmd.execute ++ end ++ ++ expected = <<-EOF ++ ++*** REMOTE GEMS *** ++ ++a (2) ++ Authors: Abraham Lincoln ., . Hirohito ++ Homepage: http://a.example.com/. ++ ++ Truncating the summary for a-2 to 100,000 characters: ++#{" This is a lot of text. This is a lot of text. This is a lot of text.\n" * 1449} This is a lot of te ++ + pl (1) + Platform: i386-linux + Author: A User +diff --git a/test/rubygems/test_gem_installer.rb b/test/rubygems/test_gem_installer.rb +index 6f8012feb8..0a439cdf3d 100644 +--- a/test/rubygems/test_gem_installer.rb ++++ b/test/rubygems/test_gem_installer.rb +@@ -1190,6 +1190,38 @@ def test_pre_install_checks_wrong_rubygems_version + end + end + ++ def test_pre_install_checks_malicious_name ++ spec = Gem::Specification.new do |s| ++ s.platform = Gem::Platform::RUBY ++ s.name = '../malicious' ++ s.version = '1' ++ s.author = 'A User' ++ s.email = 'example@example.com' ++ s.homepage = 'http://example.com' ++ s.summary = "this is a summary" ++ s.description = "This is a test description" ++ end ++ ++ Gem::Specification.reset ++ ++ def spec.full_name # so the spec is buildable ++ "malicious-1" ++ end ++ def spec.validate; end ++ ++ util_build_gem spec ++ ++ gem = File.join(@gemhome, 'cache', spec.file_name) ++ ++ use_ui @ui do ++ @installer = Gem::Installer.new gem ++ e = assert_raises Gem::InstallError do ++ @installer.pre_install_checks ++ end ++ assert_equal '# has an invalid name', e.message ++ end ++ end ++ + def test_shebang + util_make_exec @spec, "#!/usr/bin/ruby" + +diff --git a/test/rubygems/test_gem_remote_fetcher.rb b/test/rubygems/test_gem_remote_fetcher.rb +index 63dd8feb38..ca4627810b 100644 +--- a/test/rubygems/test_gem_remote_fetcher.rb ++++ b/test/rubygems/test_gem_remote_fetcher.rb +@@ -191,6 +191,21 @@ def test_api_endpoint + dns.verify + end + ++ def test_api_endpoint_ignores_trans_domain_values_that_end_with_original_in_path ++ uri = URI.parse "http://example.com/foo" ++ target = MiniTest::Mock.new ++ target.expect :target, "evil.com/a.example.com" ++ ++ dns = MiniTest::Mock.new ++ dns.expect :getresource, target, [String, Object] ++ ++ fetch = Gem::RemoteFetcher.new nil, dns ++ assert_equal URI.parse("http://example.com/foo"), fetch.api_endpoint(uri) ++ ++ target.verify ++ dns.verify ++ end ++ + def test_api_endpoint_ignores_trans_domain_values + uri = URI.parse "http://gems.example.com/foo" + target = MiniTest::Mock.new +diff --git a/test/rubygems/test_gem_specification.rb b/test/rubygems/test_gem_specification.rb +index 3cadc55d5d..4f7076a03a 100644 +--- a/test/rubygems/test_gem_specification.rb ++++ b/test/rubygems/test_gem_specification.rb +@@ -1598,7 +1598,37 @@ def test_validate_name + @a1.validate + end + +- assert_equal 'invalid value for attribute name: ":json"', e.message ++ assert_equal 'invalid value for attribute name: ":json" must be a string', e.message ++ ++ @a1.name = [] ++ e = assert_raises Gem::InvalidSpecificationException do ++ @a1.validate ++ end ++ assert_equal "invalid value for attribute name: \"[]\" must be a string", e.message ++ ++ @a1.name = "" ++ e = assert_raises Gem::InvalidSpecificationException do ++ @a1.validate ++ end ++ assert_equal "invalid value for attribute name: \"\" must include at least one letter", e.message ++ ++ @a1.name = "12345" ++ e = assert_raises Gem::InvalidSpecificationException do ++ @a1.validate ++ end ++ assert_equal "invalid value for attribute name: \"12345\" must include at least one letter", e.message ++ ++ @a1.name = "../malicious" ++ e = assert_raises Gem::InvalidSpecificationException do ++ @a1.validate ++ end ++ assert_equal "invalid value for attribute name: \"../malicious\" can only include letters, numbers, dashes, and underscores", e.message ++ ++ @a1.name = "\ba\t" ++ e = assert_raises Gem::InvalidSpecificationException do ++ @a1.validate ++ end ++ assert_equal "invalid value for attribute name: \"\\ba\\t\" can only include letters, numbers, dashes, and underscores", e.message + end + + def test_validate_non_nil +diff --git a/test/rubygems/test_gem_text.rb b/test/rubygems/test_gem_text.rb +index e5cfc41e61..9b270b481b 100644 +--- a/test/rubygems/test_gem_text.rb ++++ b/test/rubygems/test_gem_text.rb +@@ -35,6 +35,10 @@ def test_format_text_trailing # for two spaces after . + assert_equal expected, format_text(text, 78) + end + ++ def test_format_removes_nonprintable_characters ++ assert_equal "text with weird .. stuff .", format_text("text with weird \x1b\x02 stuff \x7f", 40) ++ end ++ + def test_levenshtein_distance_add + assert_equal 2, levenshtein_distance("zentest", "zntst") + assert_equal 2, levenshtein_distance("zntst", "zentest") +@@ -55,4 +59,11 @@ def test_levenshtein_distance_replace + assert_equal 7, levenshtein_distance("xxxxxxx", "ZenTest") + assert_equal 7, levenshtein_distance("zentest", "xxxxxxx") + end ++ ++ def test_truncate_text ++ assert_equal "abc", truncate_text("abc", "desc") ++ assert_equal "Truncating desc to 2 characters:\nab", truncate_text("abc", "desc", 2) ++ s = "ab" * 500_001 ++ assert_equal "Truncating desc to 1,000,000 characters:\n#{s[0, 1_000_000]}", truncate_text(s, "desc", 1_000_000) ++ end + end +-- +2.15.1 + diff --git a/SOURCES/ruby-2.2.8-sanitize-any-type-of-logs.patch b/SOURCES/ruby-2.2.8-sanitize-any-type-of-logs.patch new file mode 100644 index 0000000..bcaa558 --- /dev/null +++ b/SOURCES/ruby-2.2.8-sanitize-any-type-of-logs.patch @@ -0,0 +1,164 @@ +From 8a81d04d2588d9c7a898473b431a0dabcab39fbd Mon Sep 17 00:00:00 2001 +From: usa +Date: Thu, 14 Sep 2017 11:37:47 +0000 +Subject: [PATCH] merge revision(s) 59897: + + lib/webrick/log.rb: sanitize any type of logs + + It had failed to sanitize some type of exception messages. Reported and + patched by Yusuke Endoh (mame) at https://hackerone.com/reports/223363 + +git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_2_2@59902 b2dd03c8-39d4-4d8f-98ff-823fe69b080e +--- + ChangeLog | 7 +++++++ + lib/webrick/httpstatus.rb | 4 ---- + lib/webrick/log.rb | 4 ++-- + test/webrick/test_httpauth.rb | 36 ++++++++++++++++++++++++++++++++++++ + 4 files changed, 45 insertions(+), 6 deletions(-) + +diff --git a/ChangeLog b/ChangeLog +index a4594f678f8c..7561c35eb705 100644 +--- a/ChangeLog ++++ b/ChangeLog +@@ -4,6 +4,13 @@ + protocol list. + The protocol list from OpenSSL is not null-terminated. + patched by Kazuki Yamaguchi [Bug #11810] [ruby-core:72082] ++ ++Thu Sep 14 20:36:54 2017 Yusuke Endoh ++ ++ lib/webrick/log.rb: sanitize any type of logs ++ ++ It had failed to sanitize some type of exception messages. Reported and ++ patched by Yusuke Endoh (mame) at https://hackerone.com/reports/223363 + + Thu Sep 14 20:33:52 2017 Nobuyoshi Nakada + +diff --git a/lib/webrick/httpstatus.rb b/lib/webrick/httpstatus.rb +index 7ffda64cf0f9..5dc136f88f70 100644 +--- a/lib/webrick/httpstatus.rb ++++ b/lib/webrick/httpstatus.rb +@@ -20,10 +20,6 @@ module HTTPStatus + ## + # Root of the HTTP status class hierarchy + class Status < StandardError +- def initialize(*args) # :nodoc: +- args[0] = AccessLog.escape(args[0]) unless args.empty? +- super(*args) +- end + class << self + attr_reader :code, :reason_phrase # :nodoc: + end +diff --git a/lib/webrick/log.rb b/lib/webrick/log.rb +index 41cde4a74084..4f069ac0c549 100644 +--- a/lib/webrick/log.rb ++++ b/lib/webrick/log.rb +@@ -117,10 +117,10 @@ def debug?; @level >= DEBUG; end + # * Otherwise it will return +arg+.inspect. + def format(arg) + if arg.is_a?(Exception) +- "#{arg.class}: #{arg.message}\n\t" << ++ "#{arg.class}: #{AccessLog.escape(arg.message)}\n\t" << + arg.backtrace.join("\n\t") << "\n" + elsif arg.respond_to?(:to_str) +- arg.to_str ++ AccessLog.escape(arg.to_str) + else + arg.inspect + end +diff --git a/test/webrick/test_httpauth.rb b/test/webrick/test_httpauth.rb +index 27c37f36770b..0aebb7a231c7 100644 +--- a/test/webrick/test_httpauth.rb ++++ b/test/webrick/test_httpauth.rb +@@ -79,6 +79,43 @@ def test_basic_auth3 + WEBrick::HTTPAuth::Htpasswd.new(tmpfile.path) + } + tmpfile.close(true) ++ end ++ ++ def test_bad_username_with_control_characters ++ log_tester = lambda {|log, access_log| ++ assert_equal(2, log.length) ++ assert_match(/ERROR Basic WEBrick's realm: foo\\ebar: the user is not allowed./, log[0]) ++ assert_match(/ERROR WEBrick::HTTPStatus::Unauthorized/, log[1]) ++ } ++ TestWEBrick.start_httpserver_with_log({}, log_tester) {|server, addr, port, log| ++ realm = "WEBrick's realm" ++ path = "/basic_auth" ++ ++ Tempfile.open("test_webrick_auth") {|tmpfile| ++ tmpfile.close ++ tmp_pass = WEBrick::HTTPAuth::Htpasswd.new(tmpfile.path) ++ tmp_pass.set_passwd(realm, "webrick", "supersecretpassword") ++ tmp_pass.set_passwd(realm, "foo", "supersecretpassword") ++ tmp_pass.flush ++ ++ htpasswd = WEBrick::HTTPAuth::Htpasswd.new(tmpfile.path) ++ users = [] ++ htpasswd.each{|user, pass| users << user } ++ server.mount_proc(path){|req, res| ++ auth = WEBrick::HTTPAuth::BasicAuth.new( ++ :Realm => realm, :UserDB => htpasswd, ++ :Logger => server.logger ++ ) ++ auth.authenticate(req, res) ++ res.body = "hoge" ++ } ++ http = Net::HTTP.new(addr, port) ++ g = Net::HTTP::Get.new(path) ++ g.basic_auth("foo\ebar", "passwd") ++ http.request(g){|res| assert_not_equal("hoge", res.body, log.call) } ++ File.unlink tmpfile.path rescue nil ++ } ++ } + end + + DIGESTRES_ = / +diff --git a/test/webrick/utils.rb b/test/webrick/utils.rb +index e1c2344fb1aa..0e94ad34da71 100644 +--- a/test/webrick/utils.rb ++++ b/test/webrick/utils.rb +@@ -54,4 +54,43 @@ + def start_httpproxy(config={}, &block) + start_server(WEBrick::HTTPProxyServer, config, &block) + end ++ ++ DefaultLogTester = lambda {|log, access_log| assert_equal([], log) } ++ ++ def start_server_with_log(klass, config={}, log_tester=DefaultLogTester, &block) ++ log_ary = [] ++ access_log_ary = [] ++ log = proc { "webrick log start:\n" + (log_ary+access_log_ary).join.gsub(/^/, " ").chomp + "\nwebrick log end" } ++ server = klass.new({ ++ :BindAddress => "127.0.0.1", :Port => 0, ++ :ServerType => Thread, ++ :Logger => WEBrick::Log.new(log_ary, WEBrick::BasicLog::WARN), ++ :AccessLog => [[access_log_ary, ""]] ++ }.update(config)) ++ server_thread = server.start ++ server_thread2 = Thread.new { ++ server_thread.join ++ if log_tester ++ log_tester.call(log_ary, access_log_ary) ++ end ++ } ++ addr = server.listeners[0].addr ++ client_thread = Thread.new { ++ begin ++ block.yield([server, addr[3], addr[1], log]) ++ ensure ++ server.shutdown ++ end ++ } ++ client_thread.join ++ server_thread2.join ++ end ++ ++ def start_httpserver_with_log(config={}, log_tester=DefaultLogTester, &block) ++ start_server_with_log(WEBrick::HTTPServer, config, log_tester, &block) ++ end ++ ++ def start_httpproxy_with_log(config={}, log_tester=DefaultLogTester, &block) ++ start_server_with_log(WEBrick::HTTPProxyServer, config, log_tester, &block) ++ end + end diff --git a/SOURCES/ruby-2.2.9-Fix-a-command-injection-vulnerability-in-Net-FTP.patch b/SOURCES/ruby-2.2.9-Fix-a-command-injection-vulnerability-in-Net-FTP.patch new file mode 100644 index 0000000..77b0edf --- /dev/null +++ b/SOURCES/ruby-2.2.9-Fix-a-command-injection-vulnerability-in-Net-FTP.patch @@ -0,0 +1,386 @@ +From 0207c68ea39b74fc99e445231c1ac08ad5406720 Mon Sep 17 00:00:00 2001 +From: usa +Date: Thu, 14 Dec 2017 13:53:48 +0000 +Subject: [PATCH 1/2] merge revision(s) 61242: [Backport #14185] + + Fix a command injection vulnerability in Net::FTP. + +git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_2_2@61246 b2dd03c8-39d4-4d8f-98ff-823fe69b080e +--- + ChangeLog | 4 + + lib/net/ftp.rb | 10 +- + test/net/ftp/test_ftp.rb | 234 +++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 243 insertions(+), 5 deletions(-) + +diff --git a/ChangeLog b/ChangeLog +index 177ff95c8b..ecff5aff99 100644 +--- a/ChangeLog ++++ b/ChangeLog +@@ -1,3 +1,7 @@ ++Thu Dec 14 22:52:11 2017 Shugo Maeda ++ ++ Fix a command injection vulnerability in Net::FTP. ++ + Tue Nov 15 15:29:36 2016 NARUSE, Yui + + * ext/openssl/ossl_ssl.c (ssl_npn_select_cb_common): fix parsing +diff --git a/lib/net/ftp.rb b/lib/net/ftp.rb +index c9b80c6804..79edb80864 100644 +--- a/lib/net/ftp.rb ++++ b/lib/net/ftp.rb +@@ -607,10 +607,10 @@ module Net + if localfile + if @resume + rest_offset = File.size?(localfile) +- f = open(localfile, "a") ++ f = File.open(localfile, "a") + else + rest_offset = nil +- f = open(localfile, "w") ++ f = File.open(localfile, "w") + end + elsif !block_given? + result = "" +@@ -638,7 +638,7 @@ module Net + def gettextfile(remotefile, localfile = File.basename(remotefile)) # :yield: line + result = nil + if localfile +- f = open(localfile, "w") ++ f = File.open(localfile, "w") + elsif !block_given? + result = "" + end +@@ -684,7 +684,7 @@ module Net + else + rest_offset = nil + end +- f = open(localfile) ++ f = File.open(localfile) + begin + f.binmode + if rest_offset +@@ -703,7 +703,7 @@ module Net + # passing in the transmitted data one line at a time. + # + def puttextfile(localfile, remotefile = File.basename(localfile), &block) # :yield: line +- f = open(localfile) ++ f = File.open(localfile) + begin + storlines("STOR " + remotefile, f, &block) + ensure +diff --git a/test/net/ftp/test_ftp.rb b/test/net/ftp/test_ftp.rb +index cb311695d0..91a6002c5c 100644 +--- a/test/net/ftp/test_ftp.rb ++++ b/test/net/ftp/test_ftp.rb +@@ -2,6 +2,7 @@ require "net/ftp" + require "test/unit" + require "ostruct" + require "stringio" ++require "tmpdir" + + class FTPTest < Test::Unit::TestCase + SERVER_ADDR = "127.0.0.1" +@@ -783,6 +784,227 @@ class FTPTest < Test::Unit::TestCase + end + end + ++ def test_getbinaryfile_command_injection ++ skip "| is not allowed in filename on Windows" if windows? ++ [false, true].each do |resume| ++ commands = [] ++ binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3 ++ server = create_ftp_server { |sock| ++ sock.print("220 (test_ftp).\r\n") ++ commands.push(sock.gets) ++ sock.print("331 Please specify the password.\r\n") ++ commands.push(sock.gets) ++ sock.print("230 Login successful.\r\n") ++ commands.push(sock.gets) ++ sock.print("200 Switching to Binary mode.\r\n") ++ line = sock.gets ++ commands.push(line) ++ host, port = process_port_or_eprt(sock, line) ++ commands.push(sock.gets) ++ sock.print("150 Opening BINARY mode data connection for |echo hello (#{binary_data.size} bytes)\r\n") ++ conn = TCPSocket.new(host, port) ++ binary_data.scan(/.{1,1024}/nm) do |s| ++ conn.print(s) ++ end ++ conn.shutdown(Socket::SHUT_WR) ++ conn.read ++ conn.close ++ sock.print("226 Transfer complete.\r\n") ++ } ++ begin ++ chdir_to_tmpdir do ++ begin ++ ftp = Net::FTP.new ++ ftp.resume = resume ++ ftp.read_timeout = 0.2 ++ ftp.connect(SERVER_ADDR, server.port) ++ ftp.login ++ assert_match(/\AUSER /, commands.shift) ++ assert_match(/\APASS /, commands.shift) ++ assert_equal("TYPE I\r\n", commands.shift) ++ ftp.getbinaryfile("|echo hello") ++ assert_equal(binary_data, File.binread("./|echo hello")) ++ assert_match(/\A(PORT|EPRT) /, commands.shift) ++ assert_equal("RETR |echo hello\r\n", commands.shift) ++ assert_equal(nil, commands.shift) ++ ensure ++ ftp.close if ftp ++ end ++ end ++ ensure ++ server.close ++ end ++ end ++ end ++ ++ def test_gettextfile_command_injection ++ skip "| is not allowed in filename on Windows" if windows? ++ commands = [] ++ text_data = < +Date: Thu, 14 Dec 2017 15:08:49 +0000 +Subject: [PATCH 2/2] * test/net/ftp/test_ftp.rb (process_port_or_eprt): merge + a part of r56973 to pass the test introduced at previous commit. + +git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_2_2@61255 b2dd03c8-39d4-4d8f-98ff-823fe69b080e +--- + ChangeLog | 5 +++++ + test/net/ftp/test_ftp.rb | 18 ++++++++++++++++++ + 2 files changed, 23 insertions(+), 0 deletions(-) + +diff --git a/ChangeLog b/ChangeLog +index ecff5aff99..d9d9629ffa 100644 +--- a/ChangeLog ++++ b/ChangeLog +@@ -1,3 +1,8 @@ ++Fri Dec 15 00:08:26 2017 NAKAMURA Usaku ++ ++ * test/net/ftp/test_ftp.rb (process_port_or_eprt): merge a part of ++ r56973 to pass the test introduced at previous commit. ++ + Thu Dec 14 22:52:11 2017 Shugo Maeda + + Fix a command injection vulnerability in Net::FTP. +diff --git a/test/net/ftp/test_ftp.rb b/test/net/ftp/test_ftp.rb +index 91a6002c5c..52e5873d61 100644 +--- a/test/net/ftp/test_ftp.rb ++++ b/test/net/ftp/test_ftp.rb +@@ -1044,4 +1044,22 @@ EOF + end + end + end ++ ++ def process_port_or_eprt(sock, line) ++ case line ++ when /\APORT (.*)/ ++ port_args = $1.split(/,/) ++ host = port_args[0, 4].join(".") ++ port = port_args[4, 2].map(&:to_i).inject {|x, y| (x << 8) + y} ++ sock.print("200 PORT command successful.\r\n") ++ return host, port ++ when /\AEPRT \|2\|(.*?)\|(.*?)\|/ ++ host = $1 ++ port = $2.to_i ++ sock.print("200 EPRT command successful.\r\n") ++ return host, port ++ else ++ flunk "PORT or EPRT expected" ++ end ++ end + end +-- +2.15.1 + diff --git a/SOURCES/ruby-2.4.3-CVE-2017-0903-Fix-unsafe-object-deserialization-vulnerability.patch b/SOURCES/ruby-2.4.3-CVE-2017-0903-Fix-unsafe-object-deserialization-vulnerability.patch new file mode 100644 index 0000000..d448d91 --- /dev/null +++ b/SOURCES/ruby-2.4.3-CVE-2017-0903-Fix-unsafe-object-deserialization-vulnerability.patch @@ -0,0 +1,147 @@ +From 1281e56682692859e726e24fff30e44aac6f948b Mon Sep 17 00:00:00 2001 +From: nagachika +Date: Wed, 11 Oct 2017 13:48:14 +0000 +Subject: [PATCH] merge revision(s) 60149: [Backport #14003] + + Merge rubygems-2.6.14 changes. + + It fixed http://blog.rubygems.org/2017/10/09/unsafe-object-deserialization-vulnerability.html + +git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_2_4@60168 b2dd03c8-39d4-4d8f-98ff-823fe69b080e +--- + lib/rubygems.rb | 5 +++-- + lib/rubygems/config_file.rb | 2 +- + lib/rubygems/package.rb | 2 +- + lib/rubygems/package/old.rb | 2 +- + lib/rubygems/safe_yaml.rb | 48 +++++++++++++++++++++++++++++++++++++++++++ + lib/rubygems/specification.rb | 2 +- + 6 files changed, 55 insertions(+), 6 deletions(-) + create mode 100644 lib/rubygems/safe_yaml.rb + +diff --git a/lib/rubygems.rb b/lib/rubygems.rb +index 55aa85b8b2bd..0685bcb3c629 100644 +--- a/lib/rubygems.rb ++++ b/lib/rubygems.rb +@@ -574,7 +574,7 @@ def self.load_yaml + + unless test_syck + begin +- gem 'psych', '~> 1.2', '>= 1.2.1' ++ gem 'psych', '>= 2.0.0' + rescue Gem::LoadError + # It's OK if the user does not have the psych gem installed. We will + # attempt to require the stdlib version +@@ -598,6 +598,7 @@ def self.load_yaml + end + + require 'yaml' ++ require 'rubygems/safe_yaml' + + # If we're supposed to be using syck, then we may have to force + # activate it via the YAML::ENGINE API. +diff --git a/lib/rubygems/config_file.rb b/lib/rubygems/config_file.rb +index c95d7dd1f14e..63583b361615 100644 +--- a/lib/rubygems/config_file.rb ++++ b/lib/rubygems/config_file.rb +@@ -316,7 +316,7 @@ def load_file(filename) + return {} unless filename and File.exist? filename + + begin +- content = YAML.load(File.read(filename)) ++ content = Gem::SafeYAML.load(File.read(filename)) + unless content.kind_of? Hash + warn "Failed to load #{filename} because it doesn't contain valid YAML hash" + return {} +diff --git a/lib/rubygems/package.rb b/lib/rubygems/package.rb +index c36e71d800a2..77811ed5ecaa 100644 +--- a/lib/rubygems/package.rb ++++ b/lib/rubygems/package.rb +@@ -418,7 +418,7 @@ def read_checksums gem + + @checksums = gem.seek 'checksums.yaml.gz' do |entry| + Zlib::GzipReader.wrap entry do |gz_io| +- YAML.load gz_io.read ++ Gem::SafeYAML.safe_load gz_io.read + end + end + end +diff --git a/lib/rubygems/package/old.rb b/lib/rubygems/package/old.rb +index 5e722baa3540..071f7141ab78 100644 +--- a/lib/rubygems/package/old.rb ++++ b/lib/rubygems/package/old.rb +@@ -100,7 +100,7 @@ def file_list io # :nodoc: + header << line + end + +- YAML.load header ++ Gem::SafeYAML.safe_load header + end + + ## +diff --git a/lib/rubygems/safe_yaml.rb b/lib/rubygems/safe_yaml.rb +new file mode 100644 +index 000000000000..b98cfaa5e60d +--- /dev/null ++++ b/lib/rubygems/safe_yaml.rb +@@ -0,0 +1,48 @@ ++module Gem ++ ++ ### ++ # This module is used for safely loading YAML specs from a gem. The ++ # `safe_load` method defined on this module is specifically designed for ++ # loading Gem specifications. For loading other YAML safely, please see ++ # Psych.safe_load ++ ++ module SafeYAML ++ WHITELISTED_CLASSES = %w( ++ Symbol ++ Time ++ Date ++ Gem::Dependency ++ Gem::Platform ++ Gem::Requirement ++ Gem::Specification ++ Gem::Version ++ Gem::Version::Requirement ++ YAML::Syck::DefaultKey ++ Syck::DefaultKey ++ ) ++ ++ WHITELISTED_SYMBOLS = %w( ++ development ++ runtime ++ ) ++ ++ if ::YAML.respond_to? :safe_load ++ def self.safe_load input ++ ::YAML.safe_load(input, WHITELISTED_CLASSES, WHITELISTED_SYMBOLS, true) ++ end ++ ++ def self.load input ++ ::YAML.safe_load(input, [::Symbol]) ++ end ++ else ++ warn "YAML safe loading is not available. Please upgrade psych to a version that supports safe loading (>= 2.0)." ++ def self.safe_load input, *args ++ ::YAML.load input ++ end ++ ++ def self.load input ++ ::YAML.load input ++ end ++ end ++ end ++end +diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb +index 88e320c05ac9..40e3a70d476c 100644 +--- a/lib/rubygems/specification.rb ++++ b/lib/rubygems/specification.rb +@@ -910,7 +910,7 @@ def self.from_yaml(input) + Gem.load_yaml + + input = normalize_yaml_input input +- spec = YAML.load input ++ spec = Gem::SafeYAML.safe_load input + + if spec && spec.class == FalseClass then + raise Gem::EndOfYAMLException diff --git a/SOURCES/ruby-2.5.0-Disable-Tokyo-TZ-tests.patch b/SOURCES/ruby-2.5.0-Disable-Tokyo-TZ-tests.patch new file mode 100644 index 0000000..a36b897 --- /dev/null +++ b/SOURCES/ruby-2.5.0-Disable-Tokyo-TZ-tests.patch @@ -0,0 +1,30 @@ +diff --git a/test/ruby/test_time_tz.rb b/test/ruby/test_time_tz.rb +index 20a57fe7dd..5b9e5a8bde 100644 +--- a/test/ruby/test_time_tz.rb ++++ b/test/ruby/test_time_tz.rb +@@ -125,8 +125,8 @@ def test_asia_singapore + + def test_asia_tokyo + with_tz(tz="Asia/Tokyo") { +- assert_time_constructor(tz, "1951-05-06 03:00:00 +1000", :local, [1951,5,6,2,0,0]) +- assert_time_constructor(tz, "1951-05-06 03:59:59 +1000", :local, [1951,5,6,2,59,59]) ++# assert_time_constructor(tz, "1951-05-06 03:00:00 +1000", :local, [1951,5,6,2,0,0]) ++# assert_time_constructor(tz, "1951-05-06 03:59:59 +1000", :local, [1951,5,6,2,59,59]) + assert_time_constructor(tz, "2010-06-10 06:13:28 +0900", :local, [2010,6,10,6,13,28]) + } + end +@@ -319,10 +319,10 @@ def self.gen_zdump_test(data) + Asia/Singapore Sun Aug 8 16:30:00 1965 UTC = Mon Aug 9 00:00:00 1965 SGT isdst=0 gmtoff=27000 + Asia/Singapore Thu Dec 31 16:29:59 1981 UTC = Thu Dec 31 23:59:59 1981 SGT isdst=0 gmtoff=27000 + Asia/Singapore Thu Dec 31 16:30:00 1981 UTC = Fri Jan 1 00:30:00 1982 SGT isdst=0 gmtoff=28800 +-Asia/Tokyo Sat May 5 16:59:59 1951 UTC = Sun May 6 01:59:59 1951 JST isdst=0 gmtoff=32400 +-Asia/Tokyo Sat May 5 17:00:00 1951 UTC = Sun May 6 03:00:00 1951 JDT isdst=1 gmtoff=36000 +-Asia/Tokyo Fri Sep 7 15:59:59 1951 UTC = Sat Sep 8 01:59:59 1951 JDT isdst=1 gmtoff=36000 +-Asia/Tokyo Fri Sep 7 16:00:00 1951 UTC = Sat Sep 8 01:00:00 1951 JST isdst=0 gmtoff=32400 ++#Asia/Tokyo Sat May 5 16:59:59 1951 UTC = Sun May 6 01:59:59 1951 JST isdst=0 gmtoff=32400 ++#Asia/Tokyo Sat May 5 17:00:00 1951 UTC = Sun May 6 03:00:00 1951 JDT isdst=1 gmtoff=36000 ++#Asia/Tokyo Fri Sep 7 15:59:59 1951 UTC = Sat Sep 8 01:59:59 1951 JDT isdst=1 gmtoff=36000 ++#Asia/Tokyo Fri Sep 7 16:00:00 1951 UTC = Sat Sep 8 01:00:00 1951 JST isdst=0 gmtoff=32400 + America/St_Johns Sun Mar 11 03:30:59 2007 UTC = Sun Mar 11 00:00:59 2007 NST isdst=0 gmtoff=-12600 + America/St_Johns Sun Mar 11 03:31:00 2007 UTC = Sun Mar 11 01:01:00 2007 NDT isdst=1 gmtoff=-9000 + America/St_Johns Sun Nov 4 02:30:59 2007 UTC = Sun Nov 4 00:00:59 2007 NDT isdst=1 gmtoff=-9000 diff --git a/SOURCES/ruby-2.5.0-Fixed-command-Injection.patch b/SOURCES/ruby-2.5.0-Fixed-command-Injection.patch new file mode 100644 index 0000000..70b8a02 --- /dev/null +++ b/SOURCES/ruby-2.5.0-Fixed-command-Injection.patch @@ -0,0 +1,156 @@ +From ba0d5f7a6df6ba5545c3ce0b09e107e10d082d49 Mon Sep 17 00:00:00 2001 +From: nobu +Date: Wed, 20 Dec 2017 04:18:31 +0000 +Subject: [PATCH 1/3] Fixed command Injection + +* resolv.rb (Resolv::Hosts#lazy_initialize): fixed potential + command Injection in Hosts::new() by use of Kernel#open. + [Fix GH-1777] [ruby-core:84347] [Bug #14205] + +From: Drigg3r + +git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@61349 b2dd03c8-39d4-4d8f-98ff-823fe69b080e +--- + lib/resolv.rb | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/lib/resolv.rb b/lib/resolv.rb +index 1044b95e68..56183b837d 100644 +--- a/lib/resolv.rb ++++ b/lib/resolv.rb +@@ -186,7 +186,7 @@ def lazy_initialize # :nodoc: + unless @initialized + @name2addr = {} + @addr2name = {} +- open(@filename, 'rb') {|f| ++ File.open(@filename, 'rb') {|f| + f.each {|line| + line.sub!(/#.*/, '') + addr, hostname, *aliases = line.split(/\s+/) +-- +2.15.1 + + +From 0b6213635018ef73567388c1095ad1c556e1f4ee Mon Sep 17 00:00:00 2001 +From: nobu +Date: Wed, 20 Dec 2017 04:25:01 +0000 +Subject: [PATCH 2/3] Fixed command Injection + +* lib/resolv.rb (Resolv::Config.parse_resolv_conf): fixed + potential command injection by use of Kernel#open. + [ruby-core:84347] [Bug #14205] + +git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@61351 b2dd03c8-39d4-4d8f-98ff-823fe69b080e +--- + lib/resolv.rb | 2 +- + test/resolv/test_addr.rb | 11 +++++++++++ + test/resolv/test_dns.rb | 10 ++++++++++ + 3 files changed, 22 insertions(+), 1 deletion(-) + +diff --git a/lib/resolv.rb b/lib/resolv.rb +index 56183b837d..48ee400efe 100644 +--- a/lib/resolv.rb ++++ b/lib/resolv.rb +@@ -904,7 +904,7 @@ def Config.parse_resolv_conf(filename) + nameserver = [] + search = nil + ndots = 1 +- open(filename, 'rb') {|f| ++ File.open(filename, 'rb') {|f| + f.each {|line| + line.sub!(/[#;].*/, '') + keyword, *args = line.split(/\s+/) +diff --git a/test/resolv/test_addr.rb b/test/resolv/test_addr.rb +index 4a2df5bfca..78a28c9633 100644 +--- a/test/resolv/test_addr.rb ++++ b/test/resolv/test_addr.rb +@@ -26,4 +26,15 @@ def test_invalid_byte_comment + end + end + end ++ ++ def test_hosts_by_command ++ Dir.mktmpdir do |dir| ++ Dir.chdir(dir) do ++ hosts = Resolv::Hosts.new("|echo error") ++ assert_raise(Errno::ENOENT) do ++ hosts.each_name("") {} ++ end ++ end ++ end ++ end + end +diff --git a/test/resolv/test_dns.rb b/test/resolv/test_dns.rb +index f21a094b20..8236078374 100644 +--- a/test/resolv/test_dns.rb ++++ b/test/resolv/test_dns.rb +@@ -176,6 +176,16 @@ def test_invalid_byte_comment + end + end + ++ def test_resolv_conf_by_command ++ Dir.mktmpdir do |dir| ++ Dir.chdir(dir) do ++ assert_raise(Errno::ENOENT) do ++ Resolv::DNS::Config.parse_resolv_conf("|echo foo") ++ end ++ end ++ end ++ end ++ + def test_dots_diffences + name1 = Resolv::DNS::Name.create("example.org") + name2 = Resolv::DNS::Name.create("ex.ampl.eo.rg") +-- +2.15.1 + + +From dd71a5a9a459dbda9b9a4786f6a0b5bd59a81aae Mon Sep 17 00:00:00 2001 +From: usa +Date: Wed, 20 Dec 2017 16:04:41 +0000 +Subject: [PATCH 3/3] fix test errors on Windows + + * test/resolv/test_addr.rb (test_hosts_by_command): on Windows, `|` is + invalid charactor for path and raises `Errno::EINVAL` if trying to + open. + + * test/resolv/test_dns.rb (test_resolv_conf_by_command): ditto. + + cf. [Bug #14205] + + +git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@61374 b2dd03c8-39d4-4d8f-98ff-823fe69b080e +--- + test/resolv/test_addr.rb | 2 +- + test/resolv/test_dns.rb | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/test/resolv/test_addr.rb b/test/resolv/test_addr.rb +index 78a28c9633..14ec2651ab 100644 +--- a/test/resolv/test_addr.rb ++++ b/test/resolv/test_addr.rb +@@ -31,7 +31,7 @@ def test_hosts_by_command + Dir.mktmpdir do |dir| + Dir.chdir(dir) do + hosts = Resolv::Hosts.new("|echo error") +- assert_raise(Errno::ENOENT) do ++ assert_raise(Errno::ENOENT, Errno::EINVAL) do + hosts.each_name("") {} + end + end +diff --git a/test/resolv/test_dns.rb b/test/resolv/test_dns.rb +index 8236078374..1b44f32807 100644 +--- a/test/resolv/test_dns.rb ++++ b/test/resolv/test_dns.rb +@@ -179,7 +179,7 @@ def test_invalid_byte_comment + def test_resolv_conf_by_command + Dir.mktmpdir do |dir| + Dir.chdir(dir) do +- assert_raise(Errno::ENOENT) do ++ assert_raise(Errno::ENOENT, Errno::EINVAL) do + Resolv::DNS::Config.parse_resolv_conf("|echo foo") + end + end +-- +2.15.1 + diff --git a/SPECS/ruby.spec b/SPECS/ruby.spec index 7742f3c..4ce9dc1 100644 --- a/SPECS/ruby.spec +++ b/SPECS/ruby.spec @@ -26,7 +26,7 @@ %endif -%global release 30 +%global release 33 %{!?release_string:%global release_string %{?development_release:0.}%{release}%{?development_release:.%{development_release}}%{?dist}} %global rubygems_version 2.0.14.1 @@ -197,6 +197,47 @@ Patch35: ruby-2.1.0-fix-hostname-size-limit.patch Patch36: ruby-2.2.4-check-length-of-selected-NPN-protocol.patch # https://bugs.ruby-lang.org/issues/11810 Patch37: ruby-2.2.6-fix-parsing-protocol-list.patch +# CVE-2017-0903: Fix unsafe object deserialization through YAML formatted gem +# specifications. +# https://bugs.ruby-lang.org/issues/14003 +Patch38: ruby-2.4.3-CVE-2017-0903-Fix-unsafe-object-deserialization-vulnerability.patch +# CVE-2017-0899 - Fix an ANSI escape sequence vulnerability. +# CVE-2017-0900 - Fix a DOS vulernerability in the query command. +# CVE-2017-0901 - Fix a vulnerability in the gem installer that allowed +# a malicious gem to overwrite arbitrary files. +# CVE-2017-0902 - Fix a DNS request hijacking vulnerability. +# https://bugs.ruby-lang.org/issues/13842 +Patch39: ruby-2.2.8-lib-rubygems-fix-several-vulnerabilities-in-RubyGems.patch +# CVE-2017-0898 - Buffer underrun vulnerability in Kernel.sprintf +# https://bugs.ruby-lang.org/issues/13499 +Patch40: ruby-2.2.8-Buffer-underrun-vulnerability-in-Kernel.sprintf.patch +# CVE-2017-10784 - Escape sequence injection vulnerability in the Basic +# authentication of WEBrick +# https://github.com/ruby/ruby/commit/8a81d04d2588d9c7a898473b431a0dabcab39fbd +Patch41: ruby-2.2.8-sanitize-any-type-of-logs.patch +# CVE-2017-14064 - Arbitrary heap exposure during a JSON.generate call +# https://bugs.ruby-lang.org/issues/13853 +Patch42: ruby-2.2.8-Fix-arbitrary-heap-exposure-during-a-JSON.generate-call.patch +# CVE-2017-17405 - Command injection vulnerability in Net::FTP +# https://bugs.ruby-lang.org/issues/14185 +Patch43: ruby-2.2.9-Fix-a-command-injection-vulnerability-in-Net-FTP.patch +# CVE-2017-14033 - Buffer underrun in OpenSSL ASN1 decode. +# https://github.com/ruby/ruby/commit/5450329ad1778d72f117b68e5edb97ae1bf4d438 +Patch44: ruby-2.2.8-asn1-fix-out-of-bounds-read-in-decoding-constructed-objects.patch +# CVE-2017-17790 - Command injection in lib/resolv.rb:lazy_initialize() allows +# arbitrary code execution +# https://bugs.ruby-lang.org/issues/14205 +Patch45: ruby-2.5.0-Fixed-command-Injection.patch +# Patch for CVE-2017-0903 depends on Psych.safe_load method, which should be +# available in Psych 2.0.0, which is being part of Ruby 2.0.0, but that is +# apparently not true :/ +# https://github.com/ruby/ruby/commit/476a62fbbec0c8b7dafb74827447cfb4ebd7dd06 +Patch46: ruby-2.1.0-there-should-be-only-one-exception.patch +# https://github.com/ruby/ruby/commit/7ceafcbdf5bd2155704839f97b869e689f66feeb +Patch47: ruby-2.1.0-Adding-Psych.safe_load.patch +# Recent tzdata change breaks Ruby test suite. +# https://bugs.ruby-lang.org/issues/14438 +Patch48: ruby-2.5.0-Disable-Tokyo-TZ-tests.patch Requires: %{name}-libs%{?_isa} = %{version}-%{release} Requires: ruby(rubygems) >= %{rubygems_version} @@ -476,6 +517,17 @@ Tcl/Tk interface for the object-oriented scripting language Ruby. %patch35 -p1 %patch36 -p1 %patch37 -p1 +%patch38 -p1 +%patch39 -p1 +%patch40 -p1 +%patch41 -p1 +%patch42 -p1 +%patch43 -p1 +%patch44 -p1 +%patch45 -p1 +%patch46 -p1 +%patch47 -p1 +%patch48 -p1 # Provide an example of usage of the tapset: cp -a %{SOURCE3} . @@ -964,6 +1016,54 @@ OPENSSL_ENABLE_MD5_VERIFY=1 make check TESTS="-v $DISABLE_TESTS" %{ruby_libdir}/tkextlib %changelog +* Mon Feb 19 2018 Vít Ondruch - 2.0.0.648-33 +- Fix always passing WEBrick test. + +* Fri Feb 16 2018 Vít Ondruch - 2.0.0.648-32 +- Add Psych.safe_load + * ruby-2.1.0-there-should-be-only-one-exception.patch + * ruby-2.1.0-Adding-Psych.safe_load.patch + Related: CVE-2017-0903 +- Disable Tokyo TZ tests broken by recen tzdata update. + * ruby-2.5.0-Disable-Tokyo-TZ-tests.patch + Related: CVE-2017-0903 + +* Mon Jan 15 2018 Vít Ondruch - 2.0.0.648-31 +- Fix unsafe object deserialization in RubyGems (CVE-2017-0903). + * ruby-2.4.3-CVE-2017-0903-Fix-unsafe-object-deserialization + -vulnerability.patch + Resolves: CVE-2017-0903 +- Fix an ANSI escape sequence vulnerability (CVE-2017-0899). + Resolves: CVE-2017-0899 +- Fix a DOS vulernerability in the query command (CVE-2017-0900). + Resolves: CVE-2017-0900 +- Fix a vulnerability in the gem installer that allowed a malicious gem + to overwrite arbitrary files (CVE-2017-0901). + Resolves: CVE-2017-0901 +- Fix a DNS request hijacking vulnerability (CVE-2017-0902). + * ruby-2.2.8-lib-rubygems-fix-several-vulnerabilities-in-RubyGems.patch + Resolves: CVE-2017-0902 +- Fix buffer underrun vulnerability in Kernel.sprintf (CVE-2017-0898). + * ruby-2.2.8-Buffer-underrun-vulnerability-in-Kernel.sprintf.patch + Resolves: CVE-2017-0898 +- Escape sequence injection vulnerability in the Basic + authentication of WEBrick (CVE-2017-10784). + * ruby-2.2.8-sanitize-any-type-of-logs.patch + Resolves: CVE-2017-10784 +- Arbitrary heap exposure during a JSON.generate call (CVE-2017-14064). + * ruby-2.2.8-Fix-arbitrary-heap-exposure-during-a-JSON.generate-call.patch + Resolves: CVE-2017-14064 +- Command injection vulnerability in Net::FTP (CVE-2017-17405). + * ruby-2.2.9-Fix-a-command-injection-vulnerability-in-Net-FTP.patch + Resolves: CVE-2017-17405 +- Buffer underrun in OpenSSL ASN1 decode (CVE-2017-14033). + * ruby-2.2.8-asn1-fix-out-of-bounds-read-in-decoding-constructed-objects.patch + Resolves: CVE-2017-14033 +- Command injection in lib/resolv.rb:lazy_initialize() allows arbitrary code + execution(CVE-2017-17790). + * ruby-2.5.0-Fixed-command-Injection.patch + Resolves: CVE-2017-17790 + * Wed Mar 01 2017 Vít Ondruch - 2.0.0.648-30 - Fix test_npn_protocol_selection_ary and test_npn_protocol_selection_enum failures with newest openssl.