safe_yaml 0.9.7 → 1.0.0rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/README.md +9 -0
- data/Rakefile +13 -3
- data/bundle_install_all_ruby_versions.sh +18 -0
- data/lib/safe_yaml.rb +18 -185
- data/lib/safe_yaml/load.rb +183 -0
- data/lib/safe_yaml/parse/date.rb +9 -1
- data/lib/safe_yaml/psych_handler.rb +4 -2
- data/lib/safe_yaml/safe_to_ruby_visitor.rb +13 -1
- data/lib/safe_yaml/version.rb +1 -1
- data/run_specs_all_ruby_versions.sh +18 -12
- data/spec/issue49.yml +0 -0
- data/spec/resolver_specs.rb +5 -0
- data/spec/safe_yaml_spec.rb +11 -3
- data/spec/spec_helper.rb +10 -1
- data/spec/transform/to_date_spec.rb +7 -0
- data/spec/yaml_spec.rb +15 -0
- metadata +10 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 580842b6e20aa79420ad2b898f635bab6d29656c
|
4
|
+
data.tar.gz: 487551f91fc91dbbe0ccd00eae31ded1953a7ab3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 51987bc5bf1d65dd88b90a2837bf7006ed0bb61d663c60e8f4f05cd3a2d864bdfd3feb386e47df1bbc81f495627aa739f745a1436d874d3fdeda6a1595c0eb18
|
7
|
+
data.tar.gz: f4d748e76546f251788d7e8b2f83b12592773b08ee95132807656bc195583b0c8b017a34468b14bdceb63540b06c78e5c789ba60439fedf992053377bf50d5d7
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -97,6 +97,15 @@ The most important option is the `:safe` option (default: `true`), which control
|
|
97
97
|
|
98
98
|
All of the above options can be set at the global level via `SafeYAML::OPTIONS`. You can also set each one individually per call to `YAML.load`; an option explicitly passed to `load` will take precedence over an option specified globally.
|
99
99
|
|
100
|
+
What if I don't *want* to patch `YAML`?
|
101
|
+
---------------------------------------
|
102
|
+
|
103
|
+
[Excellent question](https://github.com/dtao/safe_yaml/issues/47)! You can also get the methods `SafeYAML.load` and `SafeYAML.load_file` without touching the `YAML` module at all like this:
|
104
|
+
|
105
|
+
require "safe_yaml/load"
|
106
|
+
|
107
|
+
This way, you can use `SafeYAML.load` to parse YAML that *you* don't trust, without affecting the rest of an application (if you're developing a library, for example).
|
108
|
+
|
100
109
|
Supported Types
|
101
110
|
---------------
|
102
111
|
|
data/Rakefile
CHANGED
@@ -1,13 +1,23 @@
|
|
1
1
|
require "rspec/core/rake_task"
|
2
2
|
|
3
3
|
desc "Run specs"
|
4
|
-
|
5
|
-
t.rspec_opts = %w(--color)
|
6
|
-
end
|
4
|
+
task :spec => ['spec:app', 'spec:lib']
|
7
5
|
|
8
6
|
namespace :spec do
|
9
7
|
desc "Run only specs tagged 'solo'"
|
10
8
|
RSpec::Core::RakeTask.new(:solo) do |t|
|
11
9
|
t.rspec_opts = %w(--color --tag solo)
|
12
10
|
end
|
11
|
+
|
12
|
+
desc "Run only specs tagged NOT tagged 'libraries' (for applications)"
|
13
|
+
RSpec::Core::RakeTask.new(:app) do |t|
|
14
|
+
puts "Running specs w/ monkeypatch"
|
15
|
+
t.rspec_opts = %w(--color --tag ~libraries)
|
16
|
+
end
|
17
|
+
|
18
|
+
desc "Run only specs tagged 'libraries'"
|
19
|
+
RSpec::Core::RakeTask.new(:lib) do |t|
|
20
|
+
puts "Running specs w/o monkeypatch"
|
21
|
+
t.rspec_opts = %w(--color --tag libraries)
|
22
|
+
end
|
13
23
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
[[ -s "$HOME/.rvm/scripts/rvm" ]] && . "$HOME/.rvm/scripts/rvm"
|
4
|
+
|
5
|
+
rvm use 1.8.7
|
6
|
+
bundle install
|
7
|
+
|
8
|
+
rvm use 1.9.2
|
9
|
+
bundle install
|
10
|
+
|
11
|
+
rvm use 1.9.3
|
12
|
+
bundle install
|
13
|
+
|
14
|
+
rvm use 2.0.0
|
15
|
+
bundle install
|
16
|
+
|
17
|
+
rvm use jruby
|
18
|
+
bundle install
|
data/lib/safe_yaml.rb
CHANGED
@@ -1,133 +1,4 @@
|
|
1
|
-
require "
|
2
|
-
|
3
|
-
# This needs to be defined up front in case any internal classes need to base
|
4
|
-
# their behavior off of this.
|
5
|
-
module SafeYAML
|
6
|
-
YAML_ENGINE = defined?(YAML::ENGINE) ? YAML::ENGINE.yamler : "syck"
|
7
|
-
end
|
8
|
-
|
9
|
-
require "set"
|
10
|
-
require "safe_yaml/deep"
|
11
|
-
require "safe_yaml/parse/hexadecimal"
|
12
|
-
require "safe_yaml/parse/sexagesimal"
|
13
|
-
require "safe_yaml/parse/date"
|
14
|
-
require "safe_yaml/transform/transformation_map"
|
15
|
-
require "safe_yaml/transform/to_boolean"
|
16
|
-
require "safe_yaml/transform/to_date"
|
17
|
-
require "safe_yaml/transform/to_float"
|
18
|
-
require "safe_yaml/transform/to_integer"
|
19
|
-
require "safe_yaml/transform/to_nil"
|
20
|
-
require "safe_yaml/transform/to_symbol"
|
21
|
-
require "safe_yaml/transform"
|
22
|
-
require "safe_yaml/resolver"
|
23
|
-
require "safe_yaml/syck_hack" if defined?(JRUBY_VERSION)
|
24
|
-
|
25
|
-
module SafeYAML
|
26
|
-
MULTI_ARGUMENT_YAML_LOAD = YAML.method(:load).arity != 1
|
27
|
-
|
28
|
-
DEFAULT_OPTIONS = Deep.freeze({
|
29
|
-
:default_mode => nil,
|
30
|
-
:suppress_warnings => false,
|
31
|
-
:deserialize_symbols => false,
|
32
|
-
:whitelisted_tags => [],
|
33
|
-
:custom_initializers => {},
|
34
|
-
:raise_on_unknown_tag => false
|
35
|
-
})
|
36
|
-
|
37
|
-
OPTIONS = Deep.copy(DEFAULT_OPTIONS)
|
38
|
-
|
39
|
-
module_function
|
40
|
-
def restore_defaults!
|
41
|
-
OPTIONS.clear.merge!(Deep.copy(DEFAULT_OPTIONS))
|
42
|
-
end
|
43
|
-
|
44
|
-
def tag_safety_check!(tag, options)
|
45
|
-
return if tag.nil? || tag == "!"
|
46
|
-
if options[:raise_on_unknown_tag] && !options[:whitelisted_tags].include?(tag) && !tag_is_explicitly_trusted?(tag)
|
47
|
-
raise "Unknown YAML tag '#{tag}'"
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def whitelist!(*classes)
|
52
|
-
classes.each do |klass|
|
53
|
-
whitelist_class!(klass)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def whitelist_class!(klass)
|
58
|
-
raise "#{klass} not a Class" unless klass.is_a?(::Class)
|
59
|
-
|
60
|
-
klass_name = klass.name
|
61
|
-
raise "#{klass} cannot be anonymous" if klass_name.nil? || klass_name.empty?
|
62
|
-
|
63
|
-
# Whitelist any built-in YAML tags supplied by Syck or Psych.
|
64
|
-
predefined_tag = predefined_tags[klass]
|
65
|
-
if predefined_tag
|
66
|
-
OPTIONS[:whitelisted_tags] << predefined_tag
|
67
|
-
return
|
68
|
-
end
|
69
|
-
|
70
|
-
# Exception is exceptional (har har).
|
71
|
-
tag_class = klass < Exception ? "exception" : "object"
|
72
|
-
|
73
|
-
tag_prefix = case YAML_ENGINE
|
74
|
-
when "psych" then "!ruby/#{tag_class}"
|
75
|
-
when "syck" then "tag:ruby.yaml.org,2002:#{tag_class}"
|
76
|
-
else raise "unknown YAML_ENGINE #{YAML_ENGINE}"
|
77
|
-
end
|
78
|
-
OPTIONS[:whitelisted_tags] << "#{tag_prefix}:#{klass_name}"
|
79
|
-
end
|
80
|
-
|
81
|
-
def predefined_tags
|
82
|
-
if @predefined_tags.nil?
|
83
|
-
@predefined_tags = {}
|
84
|
-
|
85
|
-
if YAML_ENGINE == "syck"
|
86
|
-
YAML.tagged_classes.each do |tag, klass|
|
87
|
-
@predefined_tags[klass] = tag
|
88
|
-
end
|
89
|
-
|
90
|
-
else
|
91
|
-
# Special tags appear to be hard-coded in Psych:
|
92
|
-
# https://github.com/tenderlove/psych/blob/v1.3.4/lib/psych/visitors/to_ruby.rb
|
93
|
-
# Fortunately, there aren't many that SafeYAML doesn't already support.
|
94
|
-
@predefined_tags.merge!({
|
95
|
-
Exception => "!ruby/exception",
|
96
|
-
Range => "!ruby/range",
|
97
|
-
Regexp => "!ruby/regexp",
|
98
|
-
})
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
@predefined_tags
|
103
|
-
end
|
104
|
-
|
105
|
-
if YAML_ENGINE == "psych"
|
106
|
-
def tag_is_explicitly_trusted?(tag)
|
107
|
-
false
|
108
|
-
end
|
109
|
-
|
110
|
-
else
|
111
|
-
TRUSTED_TAGS = Set.new([
|
112
|
-
"tag:yaml.org,2002:binary",
|
113
|
-
"tag:yaml.org,2002:bool#no",
|
114
|
-
"tag:yaml.org,2002:bool#yes",
|
115
|
-
"tag:yaml.org,2002:float",
|
116
|
-
"tag:yaml.org,2002:float#fix",
|
117
|
-
"tag:yaml.org,2002:int",
|
118
|
-
"tag:yaml.org,2002:map",
|
119
|
-
"tag:yaml.org,2002:null",
|
120
|
-
"tag:yaml.org,2002:seq",
|
121
|
-
"tag:yaml.org,2002:str",
|
122
|
-
"tag:yaml.org,2002:timestamp",
|
123
|
-
"tag:yaml.org,2002:timestamp#ymd"
|
124
|
-
]).freeze
|
125
|
-
|
126
|
-
def tag_is_explicitly_trusted?(tag)
|
127
|
-
TRUSTED_TAGS.include?(tag)
|
128
|
-
end
|
129
|
-
end
|
130
|
-
end
|
1
|
+
require "safe_yaml/load"
|
131
2
|
|
132
3
|
module YAML
|
133
4
|
def self.load_with_options(yaml, *original_arguments)
|
@@ -154,65 +25,27 @@ module YAML
|
|
154
25
|
end
|
155
26
|
end
|
156
27
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
require "safe_yaml/safe_to_ruby_visitor"
|
161
|
-
|
162
|
-
def self.safe_load(yaml, filename=nil, options={})
|
163
|
-
return false if yaml =~ /\A\s*\Z/
|
164
|
-
|
165
|
-
# If the user hasn't whitelisted any tags, we can go with this implementation which is
|
166
|
-
# significantly faster.
|
167
|
-
if (options && options[:whitelisted_tags] || SafeYAML::OPTIONS[:whitelisted_tags]).empty?
|
168
|
-
safe_handler = SafeYAML::PsychHandler.new(options) do |result|
|
169
|
-
return result
|
170
|
-
end
|
171
|
-
arguments_for_parse = [yaml]
|
172
|
-
arguments_for_parse << filename if SafeYAML::MULTI_ARGUMENT_YAML_LOAD
|
173
|
-
Psych::Parser.new(safe_handler).parse(*arguments_for_parse)
|
174
|
-
|
175
|
-
else
|
176
|
-
safe_resolver = SafeYAML::PsychResolver.new(options)
|
177
|
-
tree = SafeYAML::MULTI_ARGUMENT_YAML_LOAD ?
|
178
|
-
Psych.parse(yaml, filename) :
|
179
|
-
Psych.parse(yaml)
|
180
|
-
return safe_resolver.resolve_node(tree)
|
181
|
-
end
|
182
|
-
end
|
183
|
-
|
184
|
-
def self.safe_load_file(filename, options={})
|
185
|
-
File.open(filename, 'r:bom|utf-8') { |f| self.safe_load(f, filename, options) }
|
186
|
-
end
|
187
|
-
|
188
|
-
def self.unsafe_load_file(filename)
|
189
|
-
if SafeYAML::MULTI_ARGUMENT_YAML_LOAD
|
190
|
-
# https://github.com/tenderlove/psych/blob/v1.3.2/lib/psych.rb#L296-298
|
191
|
-
File.open(filename, 'r:bom|utf-8') { |f| self.unsafe_load(f, filename) }
|
192
|
-
else
|
193
|
-
# https://github.com/tenderlove/psych/blob/v1.2.2/lib/psych.rb#L231-233
|
194
|
-
self.unsafe_load File.open(filename)
|
195
|
-
end
|
196
|
-
end
|
197
|
-
|
198
|
-
else
|
199
|
-
require "safe_yaml/syck_resolver"
|
200
|
-
require "safe_yaml/syck_node_monkeypatch"
|
28
|
+
def self.safe_load(*args)
|
29
|
+
SafeYAML.load(*args)
|
30
|
+
end
|
201
31
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
return resolver.resolve_node(tree)
|
206
|
-
end
|
32
|
+
def self.safe_load_file(*args)
|
33
|
+
SafeYAML.load_file(*args)
|
34
|
+
end
|
207
35
|
|
208
|
-
|
209
|
-
|
36
|
+
def self.unsafe_load_file(filename)
|
37
|
+
if SafeYAML::MULTI_ARGUMENT_YAML_LOAD
|
38
|
+
# https://github.com/tenderlove/psych/blob/v1.3.2/lib/psych.rb#L296-298
|
39
|
+
File.open(filename, 'r:bom|utf-8') { |f| self.unsafe_load(f, filename) }
|
40
|
+
else
|
41
|
+
# https://github.com/tenderlove/psych/blob/v1.2.2/lib/psych.rb#L231-233
|
42
|
+
self.unsafe_load File.open(filename)
|
210
43
|
end
|
44
|
+
end
|
211
45
|
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
end
|
46
|
+
def self.unsafe_load_file(filename)
|
47
|
+
# https://github.com/indeyets/syck/blob/master/ext/ruby/lib/yaml.rb#L133-135
|
48
|
+
File.open(filename) { |f| self.unsafe_load(f) }
|
216
49
|
end
|
217
50
|
|
218
51
|
class << self
|
@@ -0,0 +1,183 @@
|
|
1
|
+
require "yaml"
|
2
|
+
|
3
|
+
# This needs to be defined up front in case any internal classes need to base
|
4
|
+
# their behavior off of this.
|
5
|
+
module SafeYAML
|
6
|
+
YAML_ENGINE = defined?(YAML::ENGINE) ? YAML::ENGINE.yamler : "syck"
|
7
|
+
end
|
8
|
+
|
9
|
+
require "set"
|
10
|
+
require "safe_yaml/deep"
|
11
|
+
require "safe_yaml/parse/hexadecimal"
|
12
|
+
require "safe_yaml/parse/sexagesimal"
|
13
|
+
require "safe_yaml/parse/date"
|
14
|
+
require "safe_yaml/transform/transformation_map"
|
15
|
+
require "safe_yaml/transform/to_boolean"
|
16
|
+
require "safe_yaml/transform/to_date"
|
17
|
+
require "safe_yaml/transform/to_float"
|
18
|
+
require "safe_yaml/transform/to_integer"
|
19
|
+
require "safe_yaml/transform/to_nil"
|
20
|
+
require "safe_yaml/transform/to_symbol"
|
21
|
+
require "safe_yaml/transform"
|
22
|
+
require "safe_yaml/resolver"
|
23
|
+
require "safe_yaml/syck_hack" if SafeYAML::YAML_ENGINE == "syck" && defined?(JRUBY_VERSION)
|
24
|
+
|
25
|
+
module SafeYAML
|
26
|
+
MULTI_ARGUMENT_YAML_LOAD = YAML.method(:load).arity != 1
|
27
|
+
|
28
|
+
DEFAULT_OPTIONS = Deep.freeze({
|
29
|
+
:default_mode => nil,
|
30
|
+
:suppress_warnings => false,
|
31
|
+
:deserialize_symbols => false,
|
32
|
+
:whitelisted_tags => [],
|
33
|
+
:custom_initializers => {},
|
34
|
+
:raise_on_unknown_tag => false
|
35
|
+
})
|
36
|
+
|
37
|
+
OPTIONS = Deep.copy(DEFAULT_OPTIONS)
|
38
|
+
|
39
|
+
module_function
|
40
|
+
def restore_defaults!
|
41
|
+
OPTIONS.clear.merge!(Deep.copy(DEFAULT_OPTIONS))
|
42
|
+
end
|
43
|
+
|
44
|
+
def tag_safety_check!(tag, options)
|
45
|
+
return if tag.nil? || tag == "!"
|
46
|
+
if options[:raise_on_unknown_tag] && !options[:whitelisted_tags].include?(tag) && !tag_is_explicitly_trusted?(tag)
|
47
|
+
raise "Unknown YAML tag '#{tag}'"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def whitelist!(*classes)
|
52
|
+
classes.each do |klass|
|
53
|
+
whitelist_class!(klass)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def whitelist_class!(klass)
|
58
|
+
raise "#{klass} not a Class" unless klass.is_a?(::Class)
|
59
|
+
|
60
|
+
klass_name = klass.name
|
61
|
+
raise "#{klass} cannot be anonymous" if klass_name.nil? || klass_name.empty?
|
62
|
+
|
63
|
+
# Whitelist any built-in YAML tags supplied by Syck or Psych.
|
64
|
+
predefined_tag = predefined_tags[klass]
|
65
|
+
if predefined_tag
|
66
|
+
OPTIONS[:whitelisted_tags] << predefined_tag
|
67
|
+
return
|
68
|
+
end
|
69
|
+
|
70
|
+
# Exception is exceptional (har har).
|
71
|
+
tag_class = klass < Exception ? "exception" : "object"
|
72
|
+
|
73
|
+
tag_prefix = case YAML_ENGINE
|
74
|
+
when "psych" then "!ruby/#{tag_class}"
|
75
|
+
when "syck" then "tag:ruby.yaml.org,2002:#{tag_class}"
|
76
|
+
else raise "unknown YAML_ENGINE #{YAML_ENGINE}"
|
77
|
+
end
|
78
|
+
OPTIONS[:whitelisted_tags] << "#{tag_prefix}:#{klass_name}"
|
79
|
+
end
|
80
|
+
|
81
|
+
def predefined_tags
|
82
|
+
if @predefined_tags.nil?
|
83
|
+
@predefined_tags = {}
|
84
|
+
|
85
|
+
if YAML_ENGINE == "syck"
|
86
|
+
YAML.tagged_classes.each do |tag, klass|
|
87
|
+
@predefined_tags[klass] = tag
|
88
|
+
end
|
89
|
+
|
90
|
+
else
|
91
|
+
# Special tags appear to be hard-coded in Psych:
|
92
|
+
# https://github.com/tenderlove/psych/blob/v1.3.4/lib/psych/visitors/to_ruby.rb
|
93
|
+
# Fortunately, there aren't many that SafeYAML doesn't already support.
|
94
|
+
@predefined_tags.merge!({
|
95
|
+
Exception => "!ruby/exception",
|
96
|
+
Range => "!ruby/range",
|
97
|
+
Regexp => "!ruby/regexp",
|
98
|
+
})
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
@predefined_tags
|
103
|
+
end
|
104
|
+
|
105
|
+
if YAML_ENGINE == "psych"
|
106
|
+
def tag_is_explicitly_trusted?(tag)
|
107
|
+
false
|
108
|
+
end
|
109
|
+
|
110
|
+
else
|
111
|
+
TRUSTED_TAGS = Set.new([
|
112
|
+
"tag:yaml.org,2002:binary",
|
113
|
+
"tag:yaml.org,2002:bool#no",
|
114
|
+
"tag:yaml.org,2002:bool#yes",
|
115
|
+
"tag:yaml.org,2002:float",
|
116
|
+
"tag:yaml.org,2002:float#fix",
|
117
|
+
"tag:yaml.org,2002:int",
|
118
|
+
"tag:yaml.org,2002:map",
|
119
|
+
"tag:yaml.org,2002:null",
|
120
|
+
"tag:yaml.org,2002:seq",
|
121
|
+
"tag:yaml.org,2002:str",
|
122
|
+
"tag:yaml.org,2002:timestamp",
|
123
|
+
"tag:yaml.org,2002:timestamp#ymd"
|
124
|
+
]).freeze
|
125
|
+
|
126
|
+
def tag_is_explicitly_trusted?(tag)
|
127
|
+
TRUSTED_TAGS.include?(tag)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
if SafeYAML::YAML_ENGINE == "psych"
|
132
|
+
require "safe_yaml/psych_handler"
|
133
|
+
require "safe_yaml/psych_resolver"
|
134
|
+
require "safe_yaml/safe_to_ruby_visitor"
|
135
|
+
|
136
|
+
def self.load(yaml, filename=nil, options={})
|
137
|
+
# If the user hasn't whitelisted any tags, we can go with this implementation which is
|
138
|
+
# significantly faster.
|
139
|
+
if (options && options[:whitelisted_tags] || SafeYAML::OPTIONS[:whitelisted_tags]).empty?
|
140
|
+
safe_handler = SafeYAML::PsychHandler.new(options) do |result|
|
141
|
+
return result
|
142
|
+
end
|
143
|
+
arguments_for_parse = [yaml]
|
144
|
+
arguments_for_parse << filename if SafeYAML::MULTI_ARGUMENT_YAML_LOAD
|
145
|
+
Psych::Parser.new(safe_handler).parse(*arguments_for_parse)
|
146
|
+
return safe_handler.result
|
147
|
+
|
148
|
+
else
|
149
|
+
safe_resolver = SafeYAML::PsychResolver.new(options)
|
150
|
+
tree = SafeYAML::MULTI_ARGUMENT_YAML_LOAD ?
|
151
|
+
Psych.parse(yaml, filename) :
|
152
|
+
Psych.parse(yaml)
|
153
|
+
return safe_resolver.resolve_node(tree)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def self.load_file(filename, options={})
|
158
|
+
if SafeYAML::MULTI_ARGUMENT_YAML_LOAD
|
159
|
+
File.open(filename, 'r:bom|utf-8') { |f| self.load(f, filename, options) }
|
160
|
+
|
161
|
+
else
|
162
|
+
# Ruby pukes on 1.9.2 if we try to open an empty file w/ 'r:bom|utf-8';
|
163
|
+
# so we'll not specify those flags here. This mirrors the behavior for
|
164
|
+
# unsafe_load_file so it's probably preferable anyway.
|
165
|
+
self.load File.open(filename), nil, options
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
else
|
170
|
+
require "safe_yaml/syck_resolver"
|
171
|
+
require "safe_yaml/syck_node_monkeypatch"
|
172
|
+
|
173
|
+
def self.load(yaml, options={})
|
174
|
+
resolver = SafeYAML::SyckResolver.new(SafeYAML::OPTIONS.merge(options || {}))
|
175
|
+
tree = YAML.parse(yaml)
|
176
|
+
return resolver.resolve_node(tree)
|
177
|
+
end
|
178
|
+
|
179
|
+
def self.load_file(filename, options={})
|
180
|
+
File.open(filename) { |f| self.load(f, options) }
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
data/lib/safe_yaml/parse/date.rb
CHANGED
@@ -17,10 +17,18 @@ module SafeYAML
|
|
17
17
|
# reasonably -- to seconds.
|
18
18
|
SEC_FRACTION_MULTIPLIER = RUBY_VERSION == "1.8.7" ? (SECONDS_PER_DAY * MICROSECONDS_PER_SECOND) : MICROSECONDS_PER_SECOND
|
19
19
|
|
20
|
+
# The DateTime class has a #to_time method in Ruby 1.9+;
|
21
|
+
# Before that we'll just need to convert DateTime to Time ourselves.
|
22
|
+
TO_TIME_AVAILABLE = DateTime.new.respond_to?(:to_time)
|
23
|
+
|
20
24
|
def self.value(value)
|
21
25
|
d = DateTime.parse(value)
|
26
|
+
|
27
|
+
return d.to_time if TO_TIME_AVAILABLE
|
28
|
+
|
22
29
|
usec = d.sec_fraction * SEC_FRACTION_MULTIPLIER
|
23
|
-
Time.utc(d.year, d.month, d.day, d.hour, d.min, d.sec, usec) - (d.offset * SECONDS_PER_DAY)
|
30
|
+
time = Time.utc(d.year, d.month, d.day, d.hour, d.min, d.sec, usec) - (d.offset * SECONDS_PER_DAY)
|
31
|
+
time.getlocal
|
24
32
|
end
|
25
33
|
end
|
26
34
|
end
|
@@ -11,10 +11,11 @@ module SafeYAML
|
|
11
11
|
@stack = []
|
12
12
|
@current_key = nil
|
13
13
|
@result = nil
|
14
|
+
@begun = false
|
14
15
|
end
|
15
16
|
|
16
17
|
def result
|
17
|
-
@result
|
18
|
+
@begun ? @result : false
|
18
19
|
end
|
19
20
|
|
20
21
|
def add_to_current_structure(value, anchor=nil, quoted=nil, tag=nil)
|
@@ -22,7 +23,8 @@ module SafeYAML
|
|
22
23
|
|
23
24
|
@anchors[anchor] = value if anchor
|
24
25
|
|
25
|
-
if
|
26
|
+
if !@begun
|
27
|
+
@begun = true
|
26
28
|
@result = value
|
27
29
|
@current_structure = @result
|
28
30
|
return
|
@@ -1,7 +1,19 @@
|
|
1
1
|
module SafeYAML
|
2
2
|
class SafeToRubyVisitor < Psych::Visitors::ToRuby
|
3
|
+
INITIALIZE_ARITY = superclass.instance_method(:initialize).arity
|
4
|
+
|
3
5
|
def initialize(resolver)
|
4
|
-
|
6
|
+
case INITIALIZE_ARITY
|
7
|
+
when 2
|
8
|
+
# https://github.com/tenderlove/psych/blob/v2.0.0/lib/psych/visitors/to_ruby.rb#L14-L28
|
9
|
+
loader = ClassLoader.new
|
10
|
+
scanner = ScalarScanner.new(loader)
|
11
|
+
super(scanner, loader)
|
12
|
+
|
13
|
+
else
|
14
|
+
super()
|
15
|
+
end
|
16
|
+
|
5
17
|
@resolver = resolver
|
6
18
|
end
|
7
19
|
|
data/lib/safe_yaml/version.rb
CHANGED
@@ -2,20 +2,26 @@
|
|
2
2
|
|
3
3
|
[[ -s "$HOME/.rvm/scripts/rvm" ]] && . "$HOME/.rvm/scripts/rvm"
|
4
4
|
|
5
|
-
rvm use 1.8.7
|
6
|
-
rake spec
|
5
|
+
rvm use 1.8.7
|
6
|
+
bundle exec rake spec
|
7
7
|
|
8
|
-
rvm use 1.9.2
|
9
|
-
YAMLER=syck rake spec
|
8
|
+
rvm use 1.9.2
|
9
|
+
YAMLER=syck bundle exec rake spec
|
10
10
|
|
11
|
-
rvm use 1.9.3
|
12
|
-
YAMLER=syck rake spec
|
11
|
+
rvm use 1.9.3
|
12
|
+
YAMLER=syck bundle exec rake spec
|
13
13
|
|
14
|
-
rvm use 1.9.2
|
15
|
-
YAMLER=psych rake spec
|
14
|
+
rvm use 1.9.2
|
15
|
+
YAMLER=psych bundle exec rake spec
|
16
16
|
|
17
|
-
rvm use 1.9.3
|
18
|
-
YAMLER=psych rake spec
|
17
|
+
rvm use 1.9.3
|
18
|
+
YAMLER=psych bundle exec rake spec
|
19
19
|
|
20
|
-
rvm use 2.0.0
|
21
|
-
YAMLER=psych rake spec
|
20
|
+
rvm use 2.0.0
|
21
|
+
YAMLER=psych bundle exec rake spec
|
22
|
+
|
23
|
+
rvm use jruby
|
24
|
+
JRUBY_OPTS=--1.8 bundle exec rake spec
|
25
|
+
|
26
|
+
rvm use jruby
|
27
|
+
JRUBY_OPTS=--1.9 bundle exec rake spec
|
data/spec/issue49.yml
ADDED
File without changes
|
data/spec/resolver_specs.rb
CHANGED
@@ -4,6 +4,11 @@ module ResolverSpecs
|
|
4
4
|
let(:resolver) { nil }
|
5
5
|
let(:result) { @result }
|
6
6
|
|
7
|
+
before :each do
|
8
|
+
# See the comment in the first before :each block in safe_yaml_spec.rb.
|
9
|
+
require "safe_yaml"
|
10
|
+
end
|
11
|
+
|
7
12
|
def parse(yaml)
|
8
13
|
tree = YAML.parse(yaml.unindent)
|
9
14
|
@result = resolver.resolve_node(tree)
|
data/spec/safe_yaml_spec.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), "spec_helper")
|
2
2
|
|
3
|
-
require "exploitable_back_door"
|
4
|
-
|
5
3
|
describe YAML do
|
6
4
|
# Essentially stolen from:
|
7
5
|
# https://github.com/rails/rails/blob/3-2-stable/activesupport/lib/active_support/core_ext/kernel/reporting.rb#L10-25
|
@@ -21,6 +19,12 @@ describe YAML do
|
|
21
19
|
end
|
22
20
|
|
23
21
|
before :each do
|
22
|
+
# Need to require this here (as opposed to somewhere up higher in the file)
|
23
|
+
# to ensure that safe_yaml isn't loaded and therefore YAML isn't monkey-
|
24
|
+
# patched, for tests that require only safe_yaml/load.
|
25
|
+
require "safe_yaml"
|
26
|
+
require "exploitable_back_door"
|
27
|
+
|
24
28
|
SafeYAML.restore_defaults!
|
25
29
|
end
|
26
30
|
|
@@ -257,7 +261,7 @@ describe YAML do
|
|
257
261
|
"grandcustom" => { "foo" => "foo", "bar" => "custom_bar", "baz" => "custom_baz" }
|
258
262
|
}
|
259
263
|
end
|
260
|
-
|
264
|
+
|
261
265
|
it "returns false when parsing an empty document" do
|
262
266
|
[
|
263
267
|
YAML.safe_load(""),
|
@@ -507,6 +511,10 @@ describe YAML do
|
|
507
511
|
object = YAML.safe_load_file "spec/exploit.1.9.2.yaml"
|
508
512
|
object.should_not be_a(ExploitableBackDoor)
|
509
513
|
end
|
514
|
+
|
515
|
+
it "returns false when parsing an empty file" do
|
516
|
+
YAML.safe_load_file("spec/issue49.yml").should == false
|
517
|
+
end
|
510
518
|
end
|
511
519
|
|
512
520
|
describe "load" do
|
data/spec/spec_helper.rb
CHANGED
@@ -10,7 +10,16 @@ if ENV["YAMLER"] && defined?(YAML::ENGINE)
|
|
10
10
|
puts "Running specs in Ruby #{RUBY_VERSION} with '#{YAML::ENGINE.yamler}' YAML engine."
|
11
11
|
end
|
12
12
|
|
13
|
-
|
13
|
+
if defined?(JRUBY_VERSION) && ENV["JRUBY_OPTS"]
|
14
|
+
puts "Running JRuby in #{RUBY_VERSION} mode."
|
15
|
+
end
|
16
|
+
|
17
|
+
# Caching references to these methods before loading safe_yaml in order to test
|
18
|
+
# that they aren't touched unless you actually require safe_yaml (see yaml_spec.rb).
|
19
|
+
ORIGINAL_YAML_LOAD = YAML.method(:load)
|
20
|
+
ORIGINAL_YAML_LOAD_FILE = YAML.method(:load_file)
|
21
|
+
|
22
|
+
require "safe_yaml/load"
|
14
23
|
require "ostruct"
|
15
24
|
require "hashie"
|
16
25
|
require "heredoc_unindent"
|
@@ -31,4 +31,11 @@ describe SafeYAML::Transform::ToDate do
|
|
31
31
|
result.should == Time.utc(2001, 12, 15, 2, 59, 43, 100000)
|
32
32
|
end
|
33
33
|
end
|
34
|
+
|
35
|
+
it "converts times to the local timezone" do
|
36
|
+
success, result = subject.transform?("2012-12-01 10:33:45 +11:00")
|
37
|
+
success.should be_true
|
38
|
+
result.should == Time.utc(2012, 11, 30, 23, 33, 45)
|
39
|
+
result.gmt_offset.should == Time.now.gmt_offset
|
40
|
+
end
|
34
41
|
end
|
data/spec/yaml_spec.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# See https://github.com/dtao/safe_yaml/issues/47
|
2
|
+
|
3
|
+
require File.join(File.dirname(__FILE__), "spec_helper")
|
4
|
+
|
5
|
+
describe YAML do
|
6
|
+
context "when you've only required safe_yaml/load", :libraries => true do
|
7
|
+
it "YAML.load doesn't get monkey patched" do
|
8
|
+
YAML.method(:load).should == ORIGINAL_YAML_LOAD
|
9
|
+
end
|
10
|
+
|
11
|
+
it "YAML.load_file doesn't get monkey patched" do
|
12
|
+
YAML.method(:load_file).should == ORIGINAL_YAML_LOAD_FILE
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: safe_yaml
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0rc1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dan Tao
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-12-10 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Parse YAML safely, without that pesky arbitrary object deserialization
|
14
14
|
vulnerability
|
@@ -24,8 +24,10 @@ files:
|
|
24
24
|
- LICENSE.txt
|
25
25
|
- README.md
|
26
26
|
- Rakefile
|
27
|
+
- bundle_install_all_ruby_versions.sh
|
27
28
|
- lib/safe_yaml.rb
|
28
29
|
- lib/safe_yaml/deep.rb
|
30
|
+
- lib/safe_yaml/load.rb
|
29
31
|
- lib/safe_yaml/parse/date.rb
|
30
32
|
- lib/safe_yaml/parse/hexadecimal.rb
|
31
33
|
- lib/safe_yaml/parse/sexagesimal.rb
|
@@ -50,6 +52,7 @@ files:
|
|
50
52
|
- spec/exploit.1.9.2.yaml
|
51
53
|
- spec/exploit.1.9.3.yaml
|
52
54
|
- spec/issue48.txt
|
55
|
+
- spec/issue49.yml
|
53
56
|
- spec/psych_resolver_spec.rb
|
54
57
|
- spec/resolver_specs.rb
|
55
58
|
- spec/safe_yaml_spec.rb
|
@@ -61,6 +64,7 @@ files:
|
|
61
64
|
- spec/transform/to_float_spec.rb
|
62
65
|
- spec/transform/to_integer_spec.rb
|
63
66
|
- spec/transform/to_symbol_spec.rb
|
67
|
+
- spec/yaml_spec.rb
|
64
68
|
homepage: http://dtao.github.com/safe_yaml/
|
65
69
|
licenses:
|
66
70
|
- MIT
|
@@ -76,9 +80,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
76
80
|
version: 1.8.7
|
77
81
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
82
|
requirements:
|
79
|
-
- - '
|
83
|
+
- - '>'
|
80
84
|
- !ruby/object:Gem::Version
|
81
|
-
version:
|
85
|
+
version: 1.3.1
|
82
86
|
requirements: []
|
83
87
|
rubyforge_project:
|
84
88
|
rubygems_version: 2.0.6
|
@@ -90,6 +94,7 @@ test_files:
|
|
90
94
|
- spec/exploit.1.9.2.yaml
|
91
95
|
- spec/exploit.1.9.3.yaml
|
92
96
|
- spec/issue48.txt
|
97
|
+
- spec/issue49.yml
|
93
98
|
- spec/psych_resolver_spec.rb
|
94
99
|
- spec/resolver_specs.rb
|
95
100
|
- spec/safe_yaml_spec.rb
|
@@ -101,3 +106,4 @@ test_files:
|
|
101
106
|
- spec/transform/to_float_spec.rb
|
102
107
|
- spec/transform/to_integer_spec.rb
|
103
108
|
- spec/transform/to_symbol_spec.rb
|
109
|
+
- spec/yaml_spec.rb
|