safe_yaml 0.3 → 0.4

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