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 CHANGED
@@ -4,7 +4,6 @@ gemspec
4
4
 
5
5
  group :development do
6
6
  gem "heredoc_unindent"
7
- gem "pry"
8
7
  gem "rake"
9
8
  gem "rspec"
10
9
  end
data/Gemfile.lock CHANGED
@@ -1,19 +1,13 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- safe_yaml (0.2.2)
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
- *Parse YAML safely, without that pesky arbitrary code execution vulnerability.*
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
- The **safe_yaml** gem offers an alternative to `YAML.load` suitable for accepting user input. Unlike `YAML.load`, `YAML.safe_load` will *not* expose your Ruby app 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/)).
9
+ Add this line to your application's Gemfile:
9
10
 
10
- Observe!
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 ExploitableMap
27
+ class ExploitableClassBuilder
14
28
  def []=(key, value)
15
- self.class.class_eval <<-EOS
29
+ @class ||= Class.new
30
+
31
+ @class.class_eval <<-EOS
16
32
  def #{key}
17
- return "#{value}"
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
- If your application were to contain code like this and use `YAML.load` anywhere on user input, an attacker could craft a YAML string to execute any code (yes, including `system("unix command")`) on your servers:
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:ExploitableMap
49
+ > --- !ruby/hash:ExploitableClassBuilder
28
50
  > "foo; end; puts %(I'm in yr system!); def bar": "baz"
29
51
  > EOYAML
30
- => "--- !ruby/hash:ExploitableMap\n\"foo; end; puts %(I'm in yr system!); def bar\": \"baz\"\n"
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
- => #<ExploitableMap:0x007ffadca0ca10>
56
+ => #<ExploitableClassBuilder:0x007fdbbe2e25d8 @class=#<Class:0x007fdbbe2e2510>>
35
57
 
36
58
  With `YAML.safe_load`, that attacker would be thwarted:
37
59
 
38
- > YAML.safe_load(yaml)
39
- => {"foo; end; puts %(I'm in yr system!); def bar"=>"baz"}
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
@@ -2,5 +2,5 @@ require "rspec/core/rake_task"
2
2
 
3
3
  desc "Run specs"
4
4
  RSpec::Core::RakeTask.new(:spec) do |t|
5
- t.rspec_opts = %w(--color --format d)
5
+ t.rspec_opts = %w(--color)
6
6
  end
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.2"
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.resolve_tree(tree)
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
 
@@ -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(/^:\w+$/)
30
+ elsif OPTIONS[:enable_symbol_parsing] && value.match(SYMBOL_MATCHER)
21
31
  return value[1..-1].to_sym
22
32
 
23
- elsif value.match(/^\d+$/)
33
+ elsif value.match(INTEGER_MATCHER)
24
34
  return value.to_i
25
35
 
26
- elsif value.match(/^\d+(?:\.\d*)?$/) || value.match(/^\.\d+$/)
36
+ elsif value.match(FLOAT_MATCHER)
27
37
  return value.to_f
28
38
  end
29
39
  end
@@ -1,3 +1,3 @@
1
1
  module SafeYAML
2
- VERSION = "0.3"
2
+ VERSION = "0.4"
3
3
  end
@@ -0,0 +1,12 @@
1
+ #!/bin/bash
2
+
3
+ [[ -s "$HOME/.rvm/scripts/rvm" ]] && . "$HOME/.rvm/scripts/rvm"
4
+
5
+ rvm use 1.8.7@safe_yaml
6
+ rake spec
7
+
8
+ rvm use 1.9.2@safe_yaml
9
+ rake spec
10
+
11
+ rvm use 1.9.3@safe_yaml
12
+ rake spec
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 adds a ::safe_load method to Ruby's built-in YAML module to parse YAML data for only basic types (strings, symbols, numbers, arrays, and hashes).}
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($\)
@@ -0,0 +1,2 @@
1
+ --- !ruby/object:ExploitableBackDoor
2
+ foo: bar
@@ -0,0 +1,2 @@
1
+ --- !ruby/hash:ExploitableBackDoor
2
+ foo: bar
@@ -4,40 +4,44 @@ require "safe_yaml"
4
4
  require "exploitable_back_door"
5
5
 
6
6
  describe YAML do
7
- describe "load" do
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 through custom :[]= methods" do
10
- backdoor = YAML.load("--- !ruby/hash:ExploitableBackDoor\nfoo: bar\n")
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 through" do
17
- backdoor = YAML.load("--- !ruby/object:ExploitableBackDoor\nfoo: bar\n")
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.load("--- !ruby/object:ExploitableBackDoor\nfoo: bar\n")
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 "safe_load" do
32
+ describe "load" do
29
33
  it "does NOT allow exploits through objects defined in YAML w/ !ruby/hash" do
30
- object = YAML.safe_load("--- !ruby/hash:ExploitableBackDoor\nfoo: bar\n")
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.safe_load("--- !ruby/object:ExploitableBackDoor\nfoo: bar\n")
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.safe_load <<-YAML.unindent
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.safe_load <<-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.safe_load <<-YAML
72
- mysql: &foo
75
+ result = YAML.load <<-YAML
76
+ mysql: &mysql
73
77
  adapter: mysql
74
78
  pool: 30
75
79
  login: &login
76
- username: dan
77
- password: gobbledygook
78
- local: &local
79
- <<: *foo
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" => "dan",
91
- "password" => "gobbledygook"
94
+ "username" => "user",
95
+ "password" => "password123"
92
96
  },
93
- "local" => {
97
+ "development" => {
94
98
  "adapter" => "mysql",
95
99
  "pool" => 30,
96
- "username" => "dan",
97
- "password" => "gobbledygook",
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
- it "translates most values to strings" do
9
- parse "key: value"
10
- result.should == { "key" => "value" }
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
- it "translates values starting with ':' to symbols" do
14
- parse ":key: value"
15
- result.should == { :key => "value" }
16
- end
17
-
18
- it "translates valid integral numbers to integers" do
19
- parse "integer: 1"
20
- result.should == { "integer" => 1 }
21
- end
22
-
23
- it "translates valid decimal numbers to floats" do
24
- parse "float: 3.14"
25
- result.should == { "float" => 3.14 }
26
- end
27
-
28
- it "translates valid true/false values to booleans" do
29
- parse <<-YAML
30
- - yes
31
- - true
32
- - no
33
- - false
34
- YAML
35
-
36
- result.should == [true, true, false, false]
37
- end
38
-
39
- it "translates valid nulls to nil" do
40
- parse <<-YAML
41
- -
42
- - ~
43
- - null
44
- YAML
45
-
46
- result.should == [nil] * 3
47
- end
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
@@ -5,6 +5,5 @@ $LOAD_PATH << File.join(ROOT, "lib")
5
5
  $LOAD_PATH << File.join(HERE, "support")
6
6
 
7
7
  require "heredoc_unindent"
8
- require "pry"
9
8
 
10
9
  require File.join(HERE, "shared_specs")
@@ -9,7 +9,7 @@ if RUBY_VERSION < "1.9.2"
9
9
 
10
10
  def parse(yaml)
11
11
  tree = YAML.parse(yaml.unindent)
12
- @result = resolver.resolve_tree(tree)
12
+ @result = resolver.resolve_node(tree)
13
13
  end
14
14
 
15
15
  include SharedSpecs
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.3'
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-18 00:00:00.000000000 Z
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 adds a ::safe_load method to Ruby's built-in YAML module to parse
61
- YAML data for only basic types (strings, symbols, numbers, arrays, and hashes).
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