Skip to content

Commit

Permalink
Eliminate Object#untaint deprecation warnings on JRuby 9.4.0.0.
Browse files Browse the repository at this point in the history
Despite using the UntaintExt refinement that replaces Object#untaint,
JRuby 9.4.0.0 calls the original Object#untaint method and outputs
warnings as a result:

warning: Object#untaint is deprecated and will be removed in Ruby 3.2

This is caused by the unorthodox `send(:using, UntaintExt)` approach
being used to deal with issues with older JRuby versions (see #114).
JRuby 9.4.0.0 doesn't detect this as using the refinement. This will be
allowed again in JRuby 9.4.1.0 (see jruby/jruby#7599).

Replace the UntaintExt refinement with an untaint method in
RubyCoreSupport.

Change tests for handling of tainted inputs to skip when taint/untaint
is undefined or a no-op. There's no point in running these tests unless
the inputs are actually tainted. Remove the (test-only) TaintExt
refinement too.

Resolves #145.
  • Loading branch information
philr committed Jan 28, 2023
1 parent 4e5f8b7 commit 9c092f3
Show file tree
Hide file tree
Showing 11 changed files with 72 additions and 72 deletions.
25 changes: 16 additions & 9 deletions lib/tzinfo/ruby_core_support.rb
Expand Up @@ -153,16 +153,23 @@ def self.open_file(file_name, mode, opts, &block)
end


# Object#untaint is a deprecated no-op in Ruby >= 2.7 and will be removed in
# 3.2. Add a refinement to either silence the warning, or supply the method
# if needed.
# Object#untaint is deprecated and becomes a no-op in Ruby >= 2.7. It has
# been removed from Ruby 3.2.
if !Object.new.respond_to?(:untaint) || RUBY_VERSION =~ /\A(\d+)\.(\d+)(?:\.|\z)/ && ($1 == '2' && $2.to_i >= 7 || $1.to_i >= 3)
module UntaintExt
refine Object do
def untaint
self
end
end
# Returns the supplied `Object`
#
# @param o [Object] the `Object` to untaint.
# @return [Object] `o`.
def self.untaint(o)
o
end
else
# Untaints and returns the supplied `Object`.
#
# @param o [Object] the `Object` to untaint.
# @return [Object] `o`.
def self.untaint(o)
o.untaint
end
end
end
Expand Down
8 changes: 2 additions & 6 deletions lib/tzinfo/ruby_data_source.rb
@@ -1,8 +1,4 @@
module TZInfo
# Use send as a workaround for erroneous 'wrong number of arguments' errors
# with JRuby 9.0.5.0 when calling methods with Java implementations. See #114.
send(:using, RubyCoreSupport::UntaintExt) if RubyCoreSupport.const_defined?(:UntaintExt)

# A DataSource that loads data from the set of Ruby modules included in the
# TZInfo::Data library (tzinfo-data gem).
#
Expand All @@ -25,7 +21,7 @@ def initialize
data_file = File.join('', 'tzinfo', 'data.rb')
path = $".reverse_each.detect {|p| p.end_with?(data_file) }
if path
@base_path = File.join(File.dirname(path), 'data').untaint
@base_path = RubyCoreSupport.untaint(File.join(File.dirname(path), 'data'))
else
@base_path = tzinfo_data
end
Expand All @@ -45,7 +41,7 @@ def load_timezone_info(identifier)
# Untaint identifier after it has been reassigned to a new string. We
# don't want to modify the original identifier. identifier may also be
# frozen and therefore cannot be untainted.
identifier.untaint
RubyCoreSupport.untaint(identifier)

identifier = identifier.split('/')
begin
Expand Down
12 changes: 2 additions & 10 deletions lib/tzinfo/zoneinfo_data_source.rb
@@ -1,12 +1,4 @@
module TZInfo
# Use send as a workaround for an issue on JRuby 9.2.9.0 where using the
# refinement causes calls to RubyCoreSupport.file_open to fail to pass the
# block parameter.
#
# https://travis-ci.org/tzinfo/tzinfo/jobs/628812051#L1931
# https://github.com/jruby/jruby/issues/6009
send(:using, TZInfo::RubyCoreSupport::UntaintExt) if TZInfo::RubyCoreSupport.const_defined?(:UntaintExt)

# An InvalidZoneinfoDirectory exception is raised if the DataSource is
# set to a specific zoneinfo path, which is not a valid zoneinfo directory
# (i.e. a directory containing index files named iso3166.tab and zone.tab
Expand Down Expand Up @@ -229,7 +221,7 @@ def load_timezone_info(identifier)
# Untaint path rather than identifier. We don't want to modify
# identifier. identifier may also be frozen and therefore cannot be
# untainted.
path.untaint
RubyCoreSupport.untaint(path)

begin
ZoneinfoTimezoneInfo.new(identifier, path, @posix_tz_parser)
Expand Down Expand Up @@ -388,7 +380,7 @@ def load_timezone_index
def enum_timezones(dir, exclude = [], &block)
Dir.foreach(dir ? File.join(@zoneinfo_dir, dir) : @zoneinfo_dir) do |entry|
unless entry =~ /\./ || exclude.include?(entry)
entry.untaint
RubyCoreSupport.untaint(entry)
path = dir ? File.join(dir, entry) : entry
full_path = File.join(@zoneinfo_dir, path)

Expand Down
6 changes: 1 addition & 5 deletions lib/tzinfo/zoneinfo_timezone_info.rb
@@ -1,8 +1,4 @@
module TZInfo
# Use send as a workaround for erroneous 'wrong number of arguments' errors
# with JRuby 9.0.5.0 when calling methods with Java implementations. See #114.
send(:using, RubyCoreSupport::UntaintExt) if RubyCoreSupport.const_defined?(:UntaintExt)

# An InvalidZoneinfoFile exception is raised if an attempt is made to load an
# invalid zoneinfo file.
class InvalidZoneinfoFile < StandardError
Expand Down Expand Up @@ -351,7 +347,7 @@ def define_offset(index, offset)
std_offset = 0
end

offset index, utc_offset, std_offset, offset[:abbr].untaint.to_sym
offset index, utc_offset, std_offset, RubyCoreSupport.untaint(offset[:abbr]).to_sym
end

# Parses a zoneinfo file and intializes the DataTimezoneInfo structures.
Expand Down
10 changes: 6 additions & 4 deletions test/tc_country.rb
Expand Up @@ -2,10 +2,6 @@

include TZInfo

# Use send as a workaround for erroneous 'wrong number of arguments' errors with
# JRuby 9.0.5.0 when calling methods with Java implementations. See #114.
send(:using, TaintExt) if Module.const_defined?(:TaintExt)

class TCCountry < Minitest::Test
def setup
@orig_data_source = DataSource.get
Expand Down Expand Up @@ -48,6 +44,7 @@ def test_get_case
end

def test_get_tainted_loaded
skip_if_taint_is_undefined_or_no_op
Country.get('GB')

safe_test(:unavailable => :skip) do
Expand All @@ -60,6 +57,7 @@ def test_get_tainted_loaded
end

def test_get_tainted_and_frozen_loaded
skip_if_taint_is_undefined_or_no_op
Country.get('GB')

safe_test do
Expand All @@ -69,6 +67,8 @@ def test_get_tainted_and_frozen_loaded
end

def test_get_tainted_not_previously_loaded
skip_if_taint_is_undefined_or_no_op

safe_test(:unavailable => :skip) do
code = 'GB'.dup.taint
assert(code.tainted?)
Expand All @@ -79,6 +79,8 @@ def test_get_tainted_not_previously_loaded
end

def test_get_tainted_and_frozen_not_previously_loaded
skip_if_taint_is_undefined_or_no_op

safe_test do
country = Country.get('GB'.dup.taint.freeze)
assert_equal('GB', country.code)
Expand Down
10 changes: 6 additions & 4 deletions test/tc_ruby_data_source.rb
Expand Up @@ -2,10 +2,6 @@

include TZInfo

# Use send as a workaround for erroneous 'wrong number of arguments' errors with
# JRuby 9.0.5.0 when calling methods with Java implementations. See #114.
send(:using, TaintExt) if Module.const_defined?(:TaintExt)

class TCRubyDataSource < Minitest::Test
def setup
@data_source = RubyDataSource.new
Expand Down Expand Up @@ -81,6 +77,7 @@ def test_load_timezone_info_minus
end

def test_load_timezone_info_tainted
skip_if_taint_is_undefined_or_no_op
skip_if_has_bug_14060

safe_test(:unavailable => :skip) do
Expand All @@ -93,6 +90,7 @@ def test_load_timezone_info_tainted
end

def test_load_timezone_info_tainted_and_frozen
skip_if_taint_is_undefined_or_no_op
skip_if_has_bug_14060

safe_test do
Expand Down Expand Up @@ -149,6 +147,8 @@ def test_load_country_info_case
end

def test_load_country_info_tainted
skip_if_taint_is_undefined_or_no_op

safe_test(:unavailable => :skip) do
code = 'NL'.dup.taint
assert(code.tainted?)
Expand All @@ -159,6 +159,8 @@ def test_load_country_info_tainted
end

def test_load_country_info_tainted_and_frozen
skip_if_taint_is_undefined_or_no_op

safe_test do
info = @data_source.load_country_info('NL'.dup.taint.freeze)
assert_equal('NL', info.code)
Expand Down
8 changes: 4 additions & 4 deletions test/tc_timezone.rb
Expand Up @@ -2,10 +2,6 @@

include TZInfo

# Use send as a workaround for erroneous 'wrong number of arguments' errors with
# JRuby 9.0.5.0 when calling methods with Java implementations. See #114.
send(:using, TaintExt) if Module.const_defined?(:TaintExt)

class TCTimezone < Minitest::Test

class BlockCalled < StandardError
Expand Down Expand Up @@ -244,6 +240,7 @@ def test_get_proxy_invalid
end

def test_get_tainted_loaded
skip_if_taint_is_undefined_or_no_op
Timezone.get('Europe/Andorra')

safe_test(:unavailable => :skip) do
Expand All @@ -256,6 +253,7 @@ def test_get_tainted_loaded
end

def test_get_tainted_and_frozen_loaded
skip_if_taint_is_undefined_or_no_op
Timezone.get('Europe/Andorra')

safe_test do
Expand All @@ -265,6 +263,7 @@ def test_get_tainted_and_frozen_loaded
end

def test_get_tainted_not_previously_loaded
skip_if_taint_is_undefined_or_no_op
skip_if_has_bug_14060

safe_test(:unavailable => :skip) do
Expand All @@ -277,6 +276,7 @@ def test_get_tainted_not_previously_loaded
end

def test_get_tainted_and_frozen_not_previously_loaded
skip_if_taint_is_undefined_or_no_op
skip_if_has_bug_14060

safe_test do
Expand Down
25 changes: 14 additions & 11 deletions test/tc_zoneinfo_data_source.rb
Expand Up @@ -7,17 +7,8 @@

include TZInfo

# Use send as a workaround for an issue on JRuby 9.2.9.0 where using the
# refinement causes calls to RubyCoreSupport.file_open to fail to pass the block
# parameter.
#
# https://travis-ci.org/tzinfo/tzinfo/jobs/628812051#L1931
# https://github.com/jruby/jruby/issues/6009
send(:using, TZInfo::RubyCoreSupport::UntaintExt) if TZInfo::RubyCoreSupport.const_defined?(:UntaintExt)
send(:using, TaintExt) if Module.const_defined?(:TaintExt)

class TCZoneinfoDataSource < Minitest::Test
ZONEINFO_DIR = File.join(File.expand_path(File.dirname(__FILE__)), 'zoneinfo').untaint
ZONEINFO_DIR = RubyCoreSupport.untaint(File.join(File.expand_path(File.dirname(__FILE__)), 'zoneinfo'))

def setup
@orig_search_path = ZoneinfoDataSource.search_path.clone
Expand Down Expand Up @@ -662,6 +653,8 @@ def test_load_timezone_info_invalid_file_2
end

def test_load_timezone_info_tainted
skip_if_taint_is_undefined_or_no_op

safe_test(:unavailable => :skip) do
identifier = 'Europe/Amsterdam'.dup.taint
assert(identifier.tainted?)
Expand All @@ -672,13 +665,17 @@ def test_load_timezone_info_tainted
end

def test_load_timezone_info_tainted_and_frozen
skip_if_taint_is_undefined_or_no_op

safe_test do
info = @data_source.load_timezone_info('Europe/Amsterdam'.dup.taint.freeze)
assert_equal('Europe/Amsterdam', info.identifier)
end
end

def test_load_timezone_info_tainted_zoneinfo_dir_safe_mode
skip_if_taint_is_undefined_or_no_op

safe_test(:unavailable => :skip) do
assert_raises(SecurityError) do
ZoneinfoDataSource.new(@data_source.zoneinfo_dir.dup.taint)
Expand All @@ -687,6 +684,8 @@ def test_load_timezone_info_tainted_zoneinfo_dir_safe_mode
end

def test_load_timezone_info_tainted_zoneinfo_dir
skip_if_taint_is_undefined_or_no_op

data_source = ZoneinfoDataSource.new(@data_source.zoneinfo_dir.dup.taint)
info = data_source.load_timezone_info('Europe/London')
assert_kind_of(ZoneinfoTimezoneInfo, info)
Expand All @@ -697,7 +696,7 @@ def get_timezone_filenames(directory)
entries = Dir.glob(File.join(directory, '**', '*'))

entries = entries.select do |file|
file.untaint
RubyCoreSupport.untaint(file)
File.file?(file)
end

Expand Down Expand Up @@ -868,6 +867,8 @@ def test_load_country_info_case
end

def test_load_country_info_tainted
skip_if_taint_is_undefined_or_no_op

safe_test(:unavailable => :skip) do
code = 'NL'.dup.taint
assert(code.tainted?)
Expand All @@ -878,6 +879,8 @@ def test_load_country_info_tainted
end

def test_load_country_info_tainted_and_frozen
skip_if_taint_is_undefined_or_no_op

safe_test do
info = @data_source.load_country_info('NL'.dup.taint.freeze)
assert_equal('NL', info.code)
Expand Down
6 changes: 1 addition & 5 deletions test/tc_zoneinfo_timezone_info.rb
Expand Up @@ -5,10 +5,6 @@

include TZInfo

# Use send as a workaround for erroneous 'wrong number of arguments' errors with
# JRuby 9.0.5.0 when calling methods with Java implementations. See #114.
send(:using, RubyCoreSupport::UntaintExt) if RubyCoreSupport.const_defined?(:UntaintExt)

class TCZoneinfoTimezoneInfo < Minitest::Test
class FakePosixTimeZoneParser
def initialize(&block)
Expand Down Expand Up @@ -1281,7 +1277,7 @@ def test_load_in_safe_mode

tzif_test(offsets, []) do |path, format|
# untaint only required for Ruby 1.9.2
path.untaint
RubyCoreSupport.untaint(path)

safe_test do
info = ZoneinfoTimezoneInfo.new('Zone/three', path, @posix_tz_parser)
Expand Down
32 changes: 19 additions & 13 deletions test/test_utils.rb
Expand Up @@ -108,6 +108,25 @@ def skip_if_has_bug_14060
skip('Skipping test due to Ruby 2.4.4 being affected by Bug 14060 (see https://bugs.ruby-lang.org/issues/14060#note-5)')
end
end

# Object#taint is deprecated in Ruby >= 2.7 and will be removed in 3.2.
# 2.7 makes it a no-op with a warning.
# Define a method that will skip for use in tests that deal with tainted
# objects.
if Object.respond_to?(:taint)
if RUBY_VERSION >= '2.7'
def skip_if_taint_is_undefined_or_no_op
skip('Object#taint is a no-op')
end
else
def skip_if_taint_is_undefined_or_no_op
end
end
else
def skip_if_taint_is_undefined_or_no_op
skip('Object#taint is not defined')
end
end

def assert_array_same_items(expected, actual, msg = nil)
full_message = message(msg, '') { diff(expected, actual) }
Expand Down Expand Up @@ -184,19 +203,6 @@ def assert_nothing_raised(msg = nil)
end


# Object#taint is a deprecated no-op in Ruby 2.7 and outputs a warning. It will
# be removed in 3.2. Silence the warning or supply a replacement.
if TZInfo::RubyCoreSupport.const_defined?(:UntaintExt)
module TaintExt
refine Object do
def taint
self
end
end
end
end


# JRuby 1.7.5 to 1.7.9 consider DateTime instances that differ by less than
# 1 millisecond to be equivalent (https://github.com/jruby/jruby/issues/1311).
#
Expand Down
2 changes: 1 addition & 1 deletion test/ts_all_zoneinfo.rb
Expand Up @@ -3,7 +3,7 @@
# Use a zoneinfo directory containing files needed by the tests.
# The symlinks in this directory are set up in test_utils.rb.
zoneinfo_path = File.join(File.expand_path(File.dirname(__FILE__)), 'zoneinfo')
zoneinfo_path.untaint if RUBY_VERSION < '2.7'
TZInfo.const_get(:RubyCoreSupport).untaint(zoneinfo_path) if RUBY_VERSION < '2.7'
TZInfo::DataSource.set(:zoneinfo, zoneinfo_path)

require File.join(File.expand_path(File.dirname(__FILE__)), 'ts_all.rb')

0 comments on commit 9c092f3

Please sign in to comment.