safe_yaml 0.9.7 → 1.0.0rc1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|