Skip to content

Commit

Permalink
Fix SecurityErrors when loading data in safe mode.
Browse files Browse the repository at this point in the history
Recent Ruby releases will raise a "SecurityError: Insecure operation -
require" exception when requiring a relative path name in safe mode.

Calculate the full path to the data file for use in require.

Similar to the changes made for 2.0.0, but doesn't raise an exception if
tzinfo/data can't be found when RubyDataSource is initialized.
  • Loading branch information
philr committed Dec 22, 2019
1 parent f61cfe6 commit 769e268
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 17 deletions.
37 changes: 20 additions & 17 deletions lib/tzinfo/ruby_data_source.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,24 @@ class RubyDataSource < DataSource
# Whether the country index has been loaded yet.
@@country_index_loaded = false

# Initializes a new RubyDataSource instance.
def initialize
tzinfo_data = File.join('tzinfo', 'data')
begin
require(tzinfo_data)

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
else
@base_path = tzinfo_data
end
rescue LoadError
@base_path = tzinfo_data
end
end

# Returns a TimezoneInfo instance for a given identifier.
# Raises InvalidTimezoneIdentifier if the timezone is not found or the
# identifier is invalid.
Expand Down Expand Up @@ -93,27 +111,17 @@ def require_definition(identifier)
end

# Requires an index by its name.
def self.require_index(name)
def require_index(name)
require_data(*['indexes', name])
end

# Requires a file from tzinfo/data.
def require_data(*file)
self.class.require_data(*file)
end

# Requires a file from tzinfo/data.
def self.require_data(*file)
require File.join('tzinfo', 'data', *file)
require(File.join(@base_path, *file))
end

# Loads in the index of timezones if it hasn't already been loaded.
def load_timezone_index
self.class.load_timezone_index
end

# Loads in the index of timezones if it hasn't already been loaded.
def self.load_timezone_index
unless @@timezone_index_loaded
require_index('timezones')
@@timezone_index_loaded = true
Expand All @@ -122,11 +130,6 @@ def self.load_timezone_index

# Loads in the index of countries if it hasn't already been loaded.
def load_country_index
self.class.load_country_index
end

# Loads in the index of countries if it hasn't already been loaded.
def self.load_country_index
unless @@country_index_loaded
require_index('countries')
@@country_index_loaded = true
Expand Down
16 changes: 16 additions & 0 deletions test/tc_ruby_data_source.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,22 @@ def setup
@data_source = RubyDataSource.new
end

def test_initialize_not_found
# A failure to load tzinfo/data in initialize will not cause an exception.
# Attempts to load data files will subsequently fail.
code = <<-EOF
begin
ds = TZInfo::RubyDataSource.new
puts 'Initialized'
ds.load_timezone_info('Europe/London')
rescue Exception => e
puts e.class
end
EOF

assert_sub_process_returns(['Initialized', 'TZInfo::InvalidTimezoneIdentifier'], code)
end

def test_load_timezone_info_data
info = @data_source.load_timezone_info('Europe/London')
assert_kind_of(DataTimezoneInfo, info)
Expand Down

0 comments on commit 769e268

Please sign in to comment.