safe_yaml 0.3 → 0.4
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.
- data/Gemfile +0 -1
- data/Gemfile.lock +1 -9
- data/README.md +56 -13
- data/Rakefile +1 -1
- data/lib/safe_yaml.rb +38 -2
- data/lib/safe_yaml/syck_resolver.rb +2 -15
- data/lib/safe_yaml/transform.rb +13 -3
- data/lib/safe_yaml/version.rb +1 -1
- data/run_specs_all_ruby_versions.sh +12 -0
- data/safe_yaml.gemspec +1 -1
- data/spec/exploit.1.9.2.yaml +2 -0
- data/spec/exploit.1.9.3.yaml +2 -0
- data/spec/safe_yaml_spec.rb +60 -23
- data/spec/shared_specs.rb +150 -112
- data/spec/spec_helper.rb +0 -1
- data/spec/syck_resolver_spec.rb +1 -1
- metadata +9 -4
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,19 +1,13 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
safe_yaml (0.
|
4
|
+
safe_yaml (0.3)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: http://rubygems.org/
|
8
8
|
specs:
|
9
|
-
coderay (1.0.8)
|
10
9
|
diff-lcs (1.1.3)
|
11
10
|
heredoc_unindent (1.1.2)
|
12
|
-
method_source (0.8.1)
|
13
|
-
pry (0.9.11.2)
|
14
|
-
coderay (~> 1.0.5)
|
15
|
-
method_source (~> 0.8)
|
16
|
-
slop (~> 3.4)
|
17
11
|
rake (10.0.3)
|
18
12
|
rspec (2.12.0)
|
19
13
|
rspec-core (~> 2.12.0)
|
@@ -23,14 +17,12 @@ GEM
|
|
23
17
|
rspec-expectations (2.12.1)
|
24
18
|
diff-lcs (~> 1.1.3)
|
25
19
|
rspec-mocks (2.12.1)
|
26
|
-
slop (3.4.3)
|
27
20
|
|
28
21
|
PLATFORMS
|
29
22
|
ruby
|
30
23
|
|
31
24
|
DEPENDENCIES
|
32
25
|
heredoc_unindent
|
33
|
-
pry
|
34
26
|
rake
|
35
27
|
rspec
|
36
28
|
safe_yaml!
|
data/README.md
CHANGED
@@ -1,41 +1,84 @@
|
|
1
1
|
SafeYAML
|
2
2
|
========
|
3
3
|
|
4
|
-
|
4
|
+
The **SafeYAML** gem provides an alternative implementation of `YAML.load` suitable for accepting user input in Ruby applications. Unlike Ruby's built-in implementation of `YAML.load`, SafeYAML's version will not expose apps to arbitrary code execution exploits (such as [the one recently discovered in Rails](http://www.reddit.com/r/netsec/comments/167c11/serious_vulnerability_in_ruby_on_rails_allowing/)).
|
5
5
|
|
6
|
-
|
6
|
+
Installation
|
7
|
+
------------
|
7
8
|
|
8
|
-
|
9
|
+
Add this line to your application's Gemfile:
|
9
10
|
|
10
|
-
|
11
|
+
gem "safe_yaml"
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install safe_yaml
|
20
|
+
|
21
|
+
Usage
|
22
|
+
-----
|
23
|
+
|
24
|
+
Suppose your application were to contain some code like this:
|
11
25
|
|
12
26
|
```ruby
|
13
|
-
class
|
27
|
+
class ExploitableClassBuilder
|
14
28
|
def []=(key, value)
|
15
|
-
|
29
|
+
@class ||= Class.new
|
30
|
+
|
31
|
+
@class.class_eval <<-EOS
|
16
32
|
def #{key}
|
17
|
-
|
33
|
+
#{value}
|
18
34
|
end
|
19
35
|
EOS
|
20
36
|
end
|
37
|
+
|
38
|
+
def create
|
39
|
+
@class.new
|
40
|
+
end
|
21
41
|
end
|
22
42
|
```
|
23
43
|
|
24
|
-
|
44
|
+
Now, if you were to use `YAML.load` on user input anywhere in your application without the SafeYAML gem installed, an attacker could make a request with a carefully-crafted YAML string to execute arbitrary code (yes, including `system("unix command")`) on your servers.
|
45
|
+
|
46
|
+
Observe:
|
25
47
|
|
26
48
|
> yaml = <<-EOYAML
|
27
|
-
> --- !ruby/hash:
|
49
|
+
> --- !ruby/hash:ExploitableClassBuilder
|
28
50
|
> "foo; end; puts %(I'm in yr system!); def bar": "baz"
|
29
51
|
> EOYAML
|
30
|
-
=> "--- !ruby/hash:
|
52
|
+
=> "--- !ruby/hash:ExploitableClassBuilder\n\"foo; end; puts %(I'm in yr system!); def bar\": \"baz\"\n"
|
31
53
|
|
32
54
|
> YAML.load(yaml)
|
33
55
|
I'm in yr system!
|
34
|
-
=> #<
|
56
|
+
=> #<ExploitableClassBuilder:0x007fdbbe2e25d8 @class=#<Class:0x007fdbbe2e2510>>
|
35
57
|
|
36
58
|
With `YAML.safe_load`, that attacker would be thwarted:
|
37
59
|
|
38
|
-
>
|
39
|
-
=>
|
60
|
+
> require "safe_yaml"
|
61
|
+
=> true
|
62
|
+
> YAML.load(yaml)
|
63
|
+
=> {"foo; end; puts %(I'm in yr system!); def bar"=>"baz"}
|
64
|
+
|
65
|
+
Notes
|
66
|
+
-----
|
67
|
+
|
68
|
+
The way that SafeYAML works is by restricting the kinds of objects that can be deserialized via `YAML.load`. More specifically, only the following types of objects can be deserialized by default:
|
69
|
+
|
70
|
+
- Hashes
|
71
|
+
- Arrays
|
72
|
+
- Strings
|
73
|
+
- Numbers
|
74
|
+
- Booleans
|
75
|
+
- Nils
|
76
|
+
|
77
|
+
Additionally, symbols will also be deserialized if the `YAML.enable_symbol_parsing` option is set to `true`.
|
78
|
+
|
79
|
+
For scenarios where the data to be parsed is from a trusted source and it is still desirable to deserialize arbitrary Ruby objects, the original implementation of `YAML.load` is available as `YAML.orig_load`.
|
80
|
+
|
81
|
+
Requirements
|
82
|
+
------------
|
40
83
|
|
41
84
|
SafeYAML requires Ruby 1.8.7 or newer and works with both [Syck](http://www.ruby-doc.org/stdlib-1.8.7/libdoc/yaml/rdoc/YAML.html) and [Psych](http://github.com/tenderlove/psych).
|
data/Rakefile
CHANGED
data/lib/safe_yaml.rb
CHANGED
@@ -3,7 +3,20 @@ require "safe_yaml/transform"
|
|
3
3
|
require "safe_yaml/version"
|
4
4
|
|
5
5
|
module YAML
|
6
|
-
if RUBY_VERSION >= "1.9.
|
6
|
+
if RUBY_VERSION >= "1.9.3"
|
7
|
+
require "safe_yaml/psych_handler"
|
8
|
+
def self.safe_load(yaml, filename=nil)
|
9
|
+
safe_handler = SafeYAML::PsychHandler.new
|
10
|
+
Psych::Parser.new(safe_handler).parse(yaml, filename)
|
11
|
+
return safe_handler.result
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.orig_load_file(filename)
|
15
|
+
# https://github.com/tenderlove/psych/blob/master/lib/psych.rb#L298-300
|
16
|
+
File.open(filename, 'r:bom|utf-8') { |f| self.orig_load f, filename }
|
17
|
+
end
|
18
|
+
|
19
|
+
elsif RUBY_VERSION == "1.9.2"
|
7
20
|
require "safe_yaml/psych_handler"
|
8
21
|
def self.safe_load(yaml)
|
9
22
|
safe_handler = SafeYAML::PsychHandler.new
|
@@ -11,12 +24,35 @@ module YAML
|
|
11
24
|
return safe_handler.result
|
12
25
|
end
|
13
26
|
|
27
|
+
def self.orig_load_file(filename)
|
28
|
+
# https://github.com/tenderlove/psych/blob/master/lib/psych.rb#L298-300
|
29
|
+
File.open(filename, 'r:bom|utf-8') { |f| self.orig_load f }
|
30
|
+
end
|
31
|
+
|
14
32
|
else
|
15
33
|
require "safe_yaml/syck_resolver"
|
16
34
|
def self.safe_load(yaml)
|
17
35
|
safe_resolver = SafeYAML::SyckResolver.new
|
18
36
|
tree = YAML.parse(yaml)
|
19
|
-
return safe_resolver.
|
37
|
+
return safe_resolver.resolve_node(tree)
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.orig_load_file(filename)
|
41
|
+
# https://github.com/indeyets/syck/blob/master/ext/ruby/lib/yaml.rb#L133-135
|
42
|
+
File.open(filename) { |f| self.orig_load f }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class << self
|
47
|
+
alias_method :orig_load, :load
|
48
|
+
alias_method :load, :safe_load
|
49
|
+
|
50
|
+
def enable_symbol_parsing
|
51
|
+
SafeYAML::Transform::OPTIONS[:enable_symbol_parsing]
|
52
|
+
end
|
53
|
+
|
54
|
+
def enable_symbol_parsing=(value)
|
55
|
+
SafeYAML::Transform::OPTIONS[:enable_symbol_parsing] = value
|
20
56
|
end
|
21
57
|
end
|
22
58
|
end
|
@@ -1,20 +1,5 @@
|
|
1
1
|
module SafeYAML
|
2
2
|
class SyckResolver
|
3
|
-
def initialize
|
4
|
-
@anchors = {}
|
5
|
-
end
|
6
|
-
|
7
|
-
def resolve_tree(tree)
|
8
|
-
case tree.kind
|
9
|
-
when :map
|
10
|
-
return resolve_map(tree.value)
|
11
|
-
when :seq
|
12
|
-
return resolve_seq(tree.value)
|
13
|
-
else
|
14
|
-
raise "Don't know how to resolve a #{tree.kind} tree!"
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
3
|
def resolve_node(node)
|
19
4
|
case node.kind
|
20
5
|
when :map
|
@@ -23,6 +8,8 @@ module SafeYAML
|
|
23
8
|
return resolve_seq(node.value)
|
24
9
|
when :scalar
|
25
10
|
return resolve_scalar(node.value)
|
11
|
+
else
|
12
|
+
raise "Don't know how to resolve a '#{node.kind}' node!"
|
26
13
|
end
|
27
14
|
end
|
28
15
|
|
data/lib/safe_yaml/transform.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
module SafeYAML
|
2
2
|
class Transform
|
3
|
+
OPTIONS = {
|
4
|
+
:enable_symbol_parsing => false
|
5
|
+
}
|
6
|
+
|
3
7
|
PREDEFINED_VALUES = {
|
4
8
|
"" => nil,
|
5
9
|
"~" => nil,
|
@@ -12,18 +16,24 @@ module SafeYAML
|
|
12
16
|
"false" => false
|
13
17
|
}.freeze
|
14
18
|
|
19
|
+
SYMBOL_MATCHER = /^:\w+$/.freeze
|
20
|
+
|
21
|
+
INTEGER_MATCHER = /^\d+$/.freeze
|
22
|
+
|
23
|
+
FLOAT_MATCHER = /^(?:\d+(?:\.\d*)?$)|(?:^\.\d+$)/.freeze
|
24
|
+
|
15
25
|
def self.to_proper_type(value)
|
16
26
|
if value.is_a?(String)
|
17
27
|
if PREDEFINED_VALUES.include?(value.downcase)
|
18
28
|
return PREDEFINED_VALUES[value.downcase]
|
19
29
|
|
20
|
-
elsif value.match(
|
30
|
+
elsif OPTIONS[:enable_symbol_parsing] && value.match(SYMBOL_MATCHER)
|
21
31
|
return value[1..-1].to_sym
|
22
32
|
|
23
|
-
elsif value.match(
|
33
|
+
elsif value.match(INTEGER_MATCHER)
|
24
34
|
return value.to_i
|
25
35
|
|
26
|
-
elsif value.match(
|
36
|
+
elsif value.match(FLOAT_MATCHER)
|
27
37
|
return value.to_f
|
28
38
|
end
|
29
39
|
end
|
data/lib/safe_yaml/version.rb
CHANGED
data/safe_yaml.gemspec
CHANGED
@@ -7,7 +7,7 @@ Gem::Specification.new do |gem|
|
|
7
7
|
gem.authors = "Dan Tao"
|
8
8
|
gem.email = "daniel.tao@gmail.com"
|
9
9
|
gem.description = %q{Parse YAML safely, without that pesky arbitrary code execution vulnerability.}
|
10
|
-
gem.summary = %q{SameYAML
|
10
|
+
gem.summary = %q{SameYAML provides an alternative implementation of YAML.load suitable for accepting user input in Ruby applications.}
|
11
11
|
gem.homepage = "http://github.com/dtao/safe_yaml"
|
12
12
|
|
13
13
|
gem.files = `git ls-files`.split($\)
|
data/spec/safe_yaml_spec.rb
CHANGED
@@ -4,40 +4,44 @@ require "safe_yaml"
|
|
4
4
|
require "exploitable_back_door"
|
5
5
|
|
6
6
|
describe YAML do
|
7
|
-
|
7
|
+
before :each do
|
8
|
+
YAML.enable_symbol_parsing = false
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "orig_load" do
|
8
12
|
if RUBY_VERSION >= "1.9.3"
|
9
|
-
it "allows exploits through objects defined in YAML w/ !ruby/hash
|
10
|
-
backdoor = YAML.
|
13
|
+
it "allows exploits through objects defined in YAML w/ !ruby/hash via custom :[]= methods" do
|
14
|
+
backdoor = YAML.orig_load("--- !ruby/hash:ExploitableBackDoor\nfoo: bar\n")
|
11
15
|
backdoor.should be_exploited_through_setter
|
12
16
|
end
|
13
17
|
end
|
14
18
|
|
15
19
|
if RUBY_VERSION >= "1.9.2"
|
16
|
-
it "allows exploits through objects defined in YAML w/ !ruby/object
|
17
|
-
backdoor = YAML.
|
20
|
+
it "allows exploits through objects defined in YAML w/ !ruby/object via the :init_with method" do
|
21
|
+
backdoor = YAML.orig_load("--- !ruby/object:ExploitableBackDoor\nfoo: bar\n")
|
18
22
|
backdoor.should be_exploited_through_init_with
|
19
23
|
end
|
20
24
|
end
|
21
25
|
|
22
26
|
it "allows exploits through objects w/ sensitive instance variables defined in YAML w/ !ruby/object" do
|
23
|
-
backdoor = YAML.
|
27
|
+
backdoor = YAML.orig_load("--- !ruby/object:ExploitableBackDoor\nfoo: bar\n")
|
24
28
|
backdoor.should be_exploited_through_ivars
|
25
29
|
end
|
26
30
|
end
|
27
31
|
|
28
|
-
describe "
|
32
|
+
describe "load" do
|
29
33
|
it "does NOT allow exploits through objects defined in YAML w/ !ruby/hash" do
|
30
|
-
object = YAML.
|
34
|
+
object = YAML.load("--- !ruby/hash:ExploitableBackDoor\nfoo: bar\n")
|
31
35
|
object.should_not be_a(ExploitableBackDoor)
|
32
36
|
end
|
33
37
|
|
34
38
|
it "does NOT allow exploits through objects defined in YAML w/ !ruby/object" do
|
35
|
-
object = YAML.
|
39
|
+
object = YAML.load("--- !ruby/object:ExploitableBackDoor\nfoo: bar\n")
|
36
40
|
object.should_not be_a(ExploitableBackDoor)
|
37
41
|
end
|
38
42
|
|
39
43
|
it "loads a plain ol' YAML document just fine" do
|
40
|
-
result = YAML.
|
44
|
+
result = YAML.load <<-YAML.unindent
|
41
45
|
foo:
|
42
46
|
number: 1
|
43
47
|
string: Hello, there!
|
@@ -51,14 +55,14 @@ describe YAML do
|
|
51
55
|
"foo" => {
|
52
56
|
"number" => 1,
|
53
57
|
"string" => "Hello, there!",
|
54
|
-
"symbol" => :blah,
|
58
|
+
"symbol" => ":blah",
|
55
59
|
"sequence" => ["hi", "bye"]
|
56
60
|
}
|
57
61
|
}
|
58
62
|
end
|
59
63
|
|
60
64
|
it "works for YAML documents with anchors and aliases" do
|
61
|
-
result = YAML.
|
65
|
+
result = YAML.load <<-YAML
|
62
66
|
- &id001 {}
|
63
67
|
- *id001
|
64
68
|
- *id001
|
@@ -68,15 +72,15 @@ describe YAML do
|
|
68
72
|
end
|
69
73
|
|
70
74
|
it "works for YAML documents with sections" do
|
71
|
-
result = YAML.
|
72
|
-
mysql: &
|
75
|
+
result = YAML.load <<-YAML
|
76
|
+
mysql: &mysql
|
73
77
|
adapter: mysql
|
74
78
|
pool: 30
|
75
79
|
login: &login
|
76
|
-
username:
|
77
|
-
password:
|
78
|
-
|
79
|
-
<<: *
|
80
|
+
username: user
|
81
|
+
password: password123
|
82
|
+
development: &development
|
83
|
+
<<: *mysql
|
80
84
|
<<: *login
|
81
85
|
host: localhost
|
82
86
|
YAML
|
@@ -87,17 +91,50 @@ describe YAML do
|
|
87
91
|
"pool" => 30
|
88
92
|
},
|
89
93
|
"login" => {
|
90
|
-
"username" => "
|
91
|
-
"password" => "
|
94
|
+
"username" => "user",
|
95
|
+
"password" => "password123"
|
92
96
|
},
|
93
|
-
"
|
97
|
+
"development" => {
|
94
98
|
"adapter" => "mysql",
|
95
99
|
"pool" => 30,
|
96
|
-
"username" => "
|
97
|
-
"password" => "
|
100
|
+
"username" => "user",
|
101
|
+
"password" => "password123",
|
98
102
|
"host" => "localhost"
|
99
103
|
}
|
100
104
|
}
|
101
105
|
end
|
102
106
|
end
|
107
|
+
|
108
|
+
describe "orig_load_file" do
|
109
|
+
if RUBY_VERSION >= "1.9.3"
|
110
|
+
it "allows exploits through objects defined in YAML w/ !ruby/hash via custom :[]= methods" do
|
111
|
+
backdoor = YAML.orig_load_file "spec/exploit.1.9.3.yaml"
|
112
|
+
backdoor.should be_exploited_through_setter
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
if RUBY_VERSION >= "1.9.2"
|
117
|
+
it "allows exploits through objects defined in YAML w/ !ruby/object via the :init_with method" do
|
118
|
+
backdoor = YAML.orig_load_file "spec/exploit.1.9.2.yaml"
|
119
|
+
backdoor.should be_exploited_through_init_with
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
it "allows exploits through objects w/ sensitive instance variables defined in YAML w/ !ruby/object" do
|
124
|
+
backdoor = YAML.orig_load_file "spec/exploit.1.9.2.yaml"
|
125
|
+
backdoor.should be_exploited_through_ivars
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
describe "load_file" do
|
130
|
+
it "does NOT allow exploits through objects defined in YAML w/ !ruby/hash" do
|
131
|
+
object = YAML.load_file "spec/exploit.1.9.3.yaml"
|
132
|
+
object.should_not be_a(ExploitableBackDoor)
|
133
|
+
end
|
134
|
+
|
135
|
+
it "does NOT allow exploits through objects defined in YAML w/ !ruby/object" do
|
136
|
+
object = YAML.load_file "spec/exploit.1.9.2.yaml"
|
137
|
+
object.should_not be_a(ExploitableBackDoor)
|
138
|
+
end
|
139
|
+
end
|
103
140
|
end
|
data/spec/shared_specs.rb
CHANGED
@@ -5,120 +5,158 @@ require "safe_yaml/transform"
|
|
5
5
|
module SharedSpecs
|
6
6
|
def self.included(base)
|
7
7
|
base.instance_eval do
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
context "by default" do
|
9
|
+
it "translates maps to hashes" do
|
10
|
+
parse <<-YAML
|
11
|
+
potayto: potahto
|
12
|
+
tomayto: tomahto
|
13
|
+
YAML
|
14
|
+
|
15
|
+
result.should == {
|
16
|
+
"potayto" => "potahto",
|
17
|
+
"tomayto" => "tomahto"
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
it "translates most values to strings" do
|
22
|
+
parse "string: value"
|
23
|
+
result.should == { "string" => "value" }
|
24
|
+
end
|
25
|
+
|
26
|
+
it "does not deserialize symbols" do
|
27
|
+
parse ":symbol: value"
|
28
|
+
result.should == { ":symbol" => "value" }
|
29
|
+
end
|
30
|
+
|
31
|
+
it "translates valid integral numbers to integers" do
|
32
|
+
parse "integer: 1"
|
33
|
+
result.should == { "integer" => 1 }
|
34
|
+
end
|
35
|
+
|
36
|
+
it "translates valid decimal numbers to floats" do
|
37
|
+
parse "float: 3.14"
|
38
|
+
result.should == { "float" => 3.14 }
|
39
|
+
end
|
40
|
+
|
41
|
+
it "translates sequences to arrays" do
|
42
|
+
parse <<-YAML
|
43
|
+
- foo
|
44
|
+
- bar
|
45
|
+
- baz
|
46
|
+
YAML
|
47
|
+
|
48
|
+
result.should == ["foo", "bar", "baz"]
|
49
|
+
end
|
50
|
+
|
51
|
+
it "translates valid true/false values to booleans" do
|
52
|
+
parse <<-YAML
|
53
|
+
- yes
|
54
|
+
- true
|
55
|
+
- no
|
56
|
+
- false
|
57
|
+
YAML
|
58
|
+
|
59
|
+
result.should == [true, true, false, false]
|
60
|
+
end
|
61
|
+
|
62
|
+
it "translates valid nulls to nil" do
|
63
|
+
parse <<-YAML
|
64
|
+
-
|
65
|
+
- ~
|
66
|
+
- null
|
67
|
+
YAML
|
68
|
+
|
69
|
+
result.should == [nil] * 3
|
70
|
+
end
|
71
|
+
|
72
|
+
it "applies the same transformations to keys as to values" do
|
73
|
+
parse <<-YAML
|
74
|
+
foo: string
|
75
|
+
:bar: symbol
|
76
|
+
1: integer
|
77
|
+
3.14: float
|
78
|
+
YAML
|
79
|
+
|
80
|
+
result.should == {
|
81
|
+
"foo" => "string",
|
82
|
+
":bar" => "symbol",
|
83
|
+
1 => "integer",
|
84
|
+
3.14 => "float"
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
it "applies the same transformations to elements in sequences as to all values" do
|
89
|
+
parse <<-YAML
|
90
|
+
- foo
|
91
|
+
- :bar
|
92
|
+
- 1
|
93
|
+
- 3.14
|
94
|
+
YAML
|
95
|
+
|
96
|
+
result.should == ["foo", ":bar", 1, 3.14]
|
97
|
+
end
|
98
|
+
|
99
|
+
it "deals just fine with nested maps" do
|
100
|
+
parse <<-YAML
|
101
|
+
foo:
|
102
|
+
bar:
|
103
|
+
marco: polo
|
104
|
+
YAML
|
105
|
+
|
106
|
+
result.should == { "foo" => { "bar" => { "marco" => "polo" } } }
|
107
|
+
end
|
108
|
+
|
109
|
+
it "deals just fine with nested sequences" do
|
110
|
+
parse <<-YAML
|
111
|
+
- foo
|
112
|
+
-
|
113
|
+
- bar1
|
114
|
+
- bar2
|
115
|
+
-
|
116
|
+
- baz1
|
117
|
+
- baz2
|
118
|
+
YAML
|
119
|
+
|
120
|
+
result.should == ["foo", ["bar1", "bar2", ["baz1", "baz2"]]]
|
121
|
+
end
|
11
122
|
end
|
12
123
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
it "applies the same transformations to values as to keys" do
|
50
|
-
parse <<-YAML
|
51
|
-
string: value
|
52
|
-
symbol: :value
|
53
|
-
integer: 1
|
54
|
-
float: 3.14
|
55
|
-
YAML
|
56
|
-
|
57
|
-
result.should == {
|
58
|
-
"string" => "value",
|
59
|
-
"symbol" => :value,
|
60
|
-
"integer" => 1,
|
61
|
-
"float" => 3.14
|
62
|
-
}
|
63
|
-
end
|
64
|
-
|
65
|
-
it "translates sequences to arrays" do
|
66
|
-
parse <<-YAML
|
67
|
-
- foo
|
68
|
-
- bar
|
69
|
-
- baz
|
70
|
-
YAML
|
71
|
-
|
72
|
-
result.should == ["foo", "bar", "baz"]
|
73
|
-
end
|
74
|
-
|
75
|
-
it "applies the same transformations to elements in sequences as to all values" do
|
76
|
-
parse <<-YAML
|
77
|
-
- string
|
78
|
-
- :symbol
|
79
|
-
- 1
|
80
|
-
- 3.14
|
81
|
-
YAML
|
82
|
-
|
83
|
-
result.should == ["string", :symbol, 1, 3.14]
|
84
|
-
end
|
85
|
-
|
86
|
-
it "translates maps to hashes" do
|
87
|
-
parse <<-YAML
|
88
|
-
foo: blah
|
89
|
-
bar: glah
|
90
|
-
baz: flah
|
91
|
-
YAML
|
92
|
-
|
93
|
-
result.should == {
|
94
|
-
"foo" => "blah",
|
95
|
-
"bar" => "glah",
|
96
|
-
"baz" => "flah"
|
97
|
-
}
|
98
|
-
end
|
99
|
-
|
100
|
-
it "applies the same transformations to values in hashes as to all values" do
|
101
|
-
parse <<-YAML
|
102
|
-
foo: :symbol
|
103
|
-
bar: 1
|
104
|
-
baz: 3.14
|
105
|
-
YAML
|
106
|
-
|
107
|
-
result.should == {
|
108
|
-
"foo" => :symbol,
|
109
|
-
"bar" => 1,
|
110
|
-
"baz" => 3.14
|
111
|
-
}
|
112
|
-
end
|
113
|
-
|
114
|
-
it "deals just fine with nested maps" do
|
115
|
-
parse <<-YAML
|
116
|
-
foo:
|
117
|
-
bar:
|
118
|
-
marco: polo
|
119
|
-
YAML
|
120
|
-
|
121
|
-
result.should == { "foo" => { "bar" => { "marco" => "polo" } } }
|
124
|
+
context "with symbol parsing enabled" do
|
125
|
+
before :each do
|
126
|
+
YAML.enable_symbol_parsing = true
|
127
|
+
end
|
128
|
+
|
129
|
+
it "translates values starting with ':' to symbols" do
|
130
|
+
parse "symbol: :value"
|
131
|
+
result.should == { "symbol" => :value }
|
132
|
+
end
|
133
|
+
|
134
|
+
it "applies the same transformations to keys as to values" do
|
135
|
+
parse <<-YAML
|
136
|
+
foo: string
|
137
|
+
:bar: symbol
|
138
|
+
1: integer
|
139
|
+
3.14: float
|
140
|
+
YAML
|
141
|
+
|
142
|
+
result.should == {
|
143
|
+
"foo" => "string",
|
144
|
+
:bar => "symbol",
|
145
|
+
1 => "integer",
|
146
|
+
3.14 => "float"
|
147
|
+
}
|
148
|
+
end
|
149
|
+
|
150
|
+
it "applies the same transformations to elements in sequences as to all values" do
|
151
|
+
parse <<-YAML
|
152
|
+
- foo
|
153
|
+
- :bar
|
154
|
+
- 1
|
155
|
+
- 3.14
|
156
|
+
YAML
|
157
|
+
|
158
|
+
result.should == ["foo", :bar, 1, 3.14]
|
159
|
+
end
|
122
160
|
end
|
123
161
|
end
|
124
162
|
end
|
data/spec/spec_helper.rb
CHANGED
data/spec/syck_resolver_spec.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: safe_yaml
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.4'
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-01-
|
12
|
+
date: 2013-01-23 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: Parse YAML safely, without that pesky arbitrary code execution vulnerability.
|
15
15
|
email: daniel.tao@gmail.com
|
@@ -27,7 +27,10 @@ files:
|
|
27
27
|
- lib/safe_yaml/syck_resolver.rb
|
28
28
|
- lib/safe_yaml/transform.rb
|
29
29
|
- lib/safe_yaml/version.rb
|
30
|
+
- run_specs_all_ruby_versions.sh
|
30
31
|
- safe_yaml.gemspec
|
32
|
+
- spec/exploit.1.9.2.yaml
|
33
|
+
- spec/exploit.1.9.3.yaml
|
31
34
|
- spec/psych_handler_spec.rb
|
32
35
|
- spec/safe_yaml_spec.rb
|
33
36
|
- spec/shared_specs.rb
|
@@ -57,9 +60,11 @@ rubyforge_project:
|
|
57
60
|
rubygems_version: 1.8.24
|
58
61
|
signing_key:
|
59
62
|
specification_version: 3
|
60
|
-
summary: SameYAML
|
61
|
-
|
63
|
+
summary: SameYAML provides an alternative implementation of YAML.load suitable for
|
64
|
+
accepting user input in Ruby applications.
|
62
65
|
test_files:
|
66
|
+
- spec/exploit.1.9.2.yaml
|
67
|
+
- spec/exploit.1.9.3.yaml
|
63
68
|
- spec/psych_handler_spec.rb
|
64
69
|
- spec/safe_yaml_spec.rb
|
65
70
|
- spec/shared_specs.rb
|