summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md4
-rw-r--r--lib/psych.rb34
-rw-r--r--psych.gemspec1
-rw-r--r--test/psych/test_exception.rb12
-rw-r--r--test/psych/test_psych.rb46
5 files changed, 86 insertions, 11 deletions
diff --git a/README.md b/README.md
index 0fc68d9..405e955 100644
--- a/README.md
+++ b/README.md
@@ -12,8 +12,8 @@ serialize and de-serialize most Ruby objects to and from the YAML format.
## Examples
```ruby
-# Load YAML in to a Ruby object
-Psych.load('--- foo') # => 'foo'
+# Safely load YAML in to a Ruby object
+Psych.safe_load('--- foo') # => 'foo'
# Emit YAML from a Ruby object
Psych.dump("foo") # => "--- foo\n...\n"
diff --git a/lib/psych.rb b/lib/psych.rb
index fcbb056..c3292d9 100644
--- a/lib/psych.rb
+++ b/lib/psych.rb
@@ -74,12 +74,15 @@ require 'psych/class_loader'
#
# ==== Reading from a string
#
-# Psych.load("--- a") # => 'a'
-# Psych.load("---\n - a\n - b") # => ['a', 'b']
+# Psych.safe_load("--- a") # => 'a'
+# Psych.safe_load("---\n - a\n - b") # => ['a', 'b']
+# # From a trusted string:
+# Psych.load("--- !ruby/range\nbegin: 0\nend: 42\nexcl: false\n") # => 0..42
#
# ==== Reading from a file
#
-# Psych.load_file("database.yml")
+# Psych.safe_load_file("data.yml", permitted_classes: [Date])
+# Psych.load_file("trusted_database.yml")
#
# ==== Exception handling
#
@@ -548,7 +551,7 @@ module Psych
# end
# list # => ['foo', 'bar']
#
- def self.load_stream yaml, legacy_filename = NOT_GIVEN, filename: nil, fallback: []
+ def self.load_stream yaml, legacy_filename = NOT_GIVEN, filename: nil, fallback: [], **kwargs
if legacy_filename != NOT_GIVEN
warn_with_uplevel 'Passing filename with the 2nd argument of Psych.load_stream is deprecated. Use keyword argument like Psych.load_stream(yaml, filename: ...) instead.', uplevel: 1 if $VERBOSE
filename = legacy_filename
@@ -556,10 +559,10 @@ module Psych
result = if block_given?
parse_stream(yaml, filename: filename) do |node|
- yield node.to_ruby
+ yield node.to_ruby(**kwargs)
end
else
- parse_stream(yaml, filename: filename).children.map(&:to_ruby)
+ parse_stream(yaml, filename: filename).children.map { |node| node.to_ruby(**kwargs) }
end
return fallback if result.is_a?(Array) && result.empty?
@@ -570,9 +573,24 @@ module Psych
# Load the document contained in +filename+. Returns the yaml contained in
# +filename+ as a Ruby object, or if the file is empty, it returns
# the specified +fallback+ return value, which defaults to +false+.
- def self.load_file filename, fallback: false
+ #
+ # NOTE: This method *should not* be used to parse untrusted documents, such as
+ # YAML documents that are supplied via user input. Instead, please use the
+ # safe_load_file method.
+ def self.load_file filename, **kwargs
+ File.open(filename, 'r:bom|utf-8') { |f|
+ self.load f, filename: filename, **kwargs
+ }
+ end
+
+ ###
+ # Safely loads the document contained in +filename+. Returns the yaml contained in
+ # +filename+ as a Ruby object, or if the file is empty, it returns
+ # the specified +fallback+ return value, which defaults to +false+.
+ # See safe_load for options.
+ def self.safe_load_file filename, **kwargs
File.open(filename, 'r:bom|utf-8') { |f|
- self.load f, filename: filename, fallback: fallback
+ self.safe_load f, filename: filename, **kwargs
}
end
diff --git a/psych.gemspec b/psych.gemspec
index 5f4bb43..e9e36e6 100644
--- a/psych.gemspec
+++ b/psych.gemspec
@@ -46,7 +46,6 @@ DESCRIPTION
s.extra_rdoc_files = ["README.md"]
s.required_ruby_version = Gem::Requirement.new(">= 2.4.0")
- s.rubygems_version = "2.5.1"
s.required_rubygems_version = Gem::Requirement.new(">= 0")
if RUBY_ENGINE == 'jruby'
diff --git a/test/psych/test_exception.rb b/test/psych/test_exception.rb
index e355c26..78601d0 100644
--- a/test/psych/test_exception.rb
+++ b/test/psych/test_exception.rb
@@ -118,6 +118,18 @@ module Psych
}
end
+ def test_safe_load_file_exception
+ Tempfile.create(['loadfile', 'yml']) {|t|
+ t.binmode
+ t.write '--- `'
+ t.close
+ ex = assert_raises(Psych::SyntaxError) do
+ Psych.safe_load_file t.path
+ end
+ assert_equal t.path, ex.file
+ }
+ end
+
def test_psych_parse_takes_file
ex = assert_raises(Psych::SyntaxError) do
Psych.parse '--- `'
diff --git a/test/psych/test_psych.rb b/test/psych/test_psych.rb
index 55d9f19..30612de 100644
--- a/test/psych/test_psych.rb
+++ b/test/psych/test_psych.rb
@@ -125,6 +125,19 @@ class TestPsych < Psych::TestCase
assert_equal %w{ foo bar }, docs
end
+ def test_load_stream_freeze
+ docs = Psych.load_stream("--- foo\n...\n--- bar\n...", freeze: true)
+ assert_equal %w{ foo bar }, docs
+ docs.each do |string|
+ assert_predicate string, :frozen?
+ end
+ end
+
+ def test_load_stream_symbolize_names
+ docs = Psych.load_stream("---\nfoo: bar", symbolize_names: true)
+ assert_equal [{foo: 'bar'}], docs
+ end
+
def test_load_stream_default_fallback
assert_equal [], Psych.load_stream("")
end
@@ -242,6 +255,27 @@ class TestPsych < Psych::TestCase
}
end
+ def test_load_file_freeze
+ Tempfile.create(['yikes', 'yml']) {|t|
+ t.binmode
+ t.write('--- hello world')
+ t.close
+
+ object = Psych.load_file(t.path, freeze: true)
+ assert_predicate object, :frozen?
+ }
+ end
+
+ def test_load_file_symbolize_names
+ Tempfile.create(['yikes', 'yml']) {|t|
+ t.binmode
+ t.write("---\nfoo: bar")
+ t.close
+
+ assert_equal({foo: 'bar'}, Psych.load_file(t.path, symbolize_names: true))
+ }
+ end
+
def test_load_file_default_fallback
Tempfile.create(['empty', 'yml']) {|t|
assert_equal false, Psych.load_file(t.path)
@@ -285,6 +319,18 @@ class TestPsych < Psych::TestCase
}
end
+ def test_safe_load_file_with_permitted_classe
+ Tempfile.create(['false', 'yml']) {|t|
+ t.binmode
+ t.write("--- !ruby/range\nbegin: 0\nend: 42\nexcl: false\n")
+ t.close
+ assert_equal 0..42, Psych.safe_load_file(t.path, permitted_classes: [Range])
+ assert_raises(Psych::DisallowedClass) {
+ Psych.safe_load_file(t.path)
+ }
+ }
+ end
+
def test_parse_file
Tempfile.create(['yikes', 'yml']) {|t|
t.binmode