safe_yaml 0.5 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- safe_yaml (0.4)
4
+ safe_yaml (0.5.1)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
data/README.md CHANGED
@@ -55,7 +55,7 @@ Observe:
55
55
  I'm in yr system!
56
56
  => #<ExploitableClassBuilder:0x007fdbbe2e25d8 @class=#<Class:0x007fdbbe2e2510>>
57
57
 
58
- With `YAML.safe_load`, that attacker would be thwarted:
58
+ With SafeYAML, that attacker would be thwarted:
59
59
 
60
60
  > require "safe_yaml"
61
61
  => true
@@ -72,6 +72,7 @@ The way that SafeYAML works is by restricting the kinds of objects that can be d
72
72
  - Strings
73
73
  - Numbers
74
74
  - Dates
75
+ - Times
75
76
  - Booleans
76
77
  - Nils
77
78
 
data/lib/safe_yaml.rb CHANGED
@@ -1,4 +1,11 @@
1
1
  require "yaml"
2
+ require "safe_yaml/transform/to_boolean"
3
+ require "safe_yaml/transform/to_date"
4
+ require "safe_yaml/transform/to_float"
5
+ require "safe_yaml/transform/to_integer"
6
+ require "safe_yaml/transform/to_nil"
7
+ require "safe_yaml/transform/to_symbol"
8
+ require "safe_yaml/transform/to_time"
2
9
  require "safe_yaml/transform"
3
10
  require "safe_yaml/version"
4
11
 
@@ -4,43 +4,21 @@ module SafeYAML
4
4
  :enable_symbol_parsing => false
5
5
  }
6
6
 
7
- PREDEFINED_VALUES = {
8
- "" => nil,
9
- "~" => nil,
10
- "null" => nil,
11
- "yes" => true,
12
- "on" => true,
13
- "true" => true,
14
- "no" => false,
15
- "off" => false,
16
- "false" => false
17
- }.freeze
18
-
19
- SYMBOL_MATCHER = /^:\w+$/.freeze
20
-
21
- INTEGER_MATCHER = /^\d+$/.freeze
22
-
23
- FLOAT_MATCHER = /^(?:\d+(?:\.\d*)?$)|(?:^\.\d+$)/.freeze
24
-
25
- DATE_MATCHER = /^\d{4}\-\d{2}\-\d{2}$/.freeze
7
+ TRANSFORMERS = [
8
+ Transform::ToSymbol.new,
9
+ Transform::ToInteger.new,
10
+ Transform::ToFloat.new,
11
+ Transform::ToNil.new,
12
+ Transform::ToBoolean.new,
13
+ Transform::ToDate.new,
14
+ Transform::ToTime.new
15
+ ]
26
16
 
27
17
  def self.to_proper_type(value)
28
18
  if value.is_a?(String)
29
- if PREDEFINED_VALUES.include?(value.downcase)
30
- return PREDEFINED_VALUES[value.downcase]
31
-
32
- elsif OPTIONS[:enable_symbol_parsing] && value.match(SYMBOL_MATCHER)
33
- return value[1..-1].to_sym
34
-
35
- elsif value.match(INTEGER_MATCHER)
36
- return value.to_i
37
-
38
- elsif value.match(FLOAT_MATCHER)
39
- return value.to_f
40
-
41
- elsif value.match(DATE_MATCHER)
42
- date = Date.parse(value) rescue nil
43
- return date if date
19
+ TRANSFORMERS.each do |transformer|
20
+ success, transformed_value = transformer.transform?(value)
21
+ return transformed_value if success
44
22
  end
45
23
  end
46
24
 
@@ -0,0 +1,19 @@
1
+ module SafeYAML
2
+ class Transform
3
+ class ToBoolean
4
+ PREDEFINED_VALUES = {
5
+ "yes" => true,
6
+ "on" => true,
7
+ "true" => true,
8
+ "no" => false,
9
+ "off" => false,
10
+ "false" => false
11
+ }.freeze
12
+
13
+ def transform?(value)
14
+ key = value.downcase
15
+ return PREDEFINED_VALUES.include?(key), PREDEFINED_VALUES[key]
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,13 @@
1
+ module SafeYAML
2
+ class Transform
3
+ class ToDate
4
+ MATCHER = /^\d{4}\-\d{2}\-\d{2}$/.freeze
5
+
6
+ def transform?(value)
7
+ return false unless MATCHER.match(value)
8
+ date = Date.parse(value) rescue nil
9
+ return !!date, date
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ module SafeYAML
2
+ class Transform
3
+ class ToFloat
4
+ MATCHER = /^(?:\d+(?:\.\d*)?$)|(?:^\.\d+$)/.freeze
5
+
6
+ def transform?(value)
7
+ return false unless MATCHER.match(value)
8
+ return true, value.to_f
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module SafeYAML
2
+ class Transform
3
+ class ToInteger
4
+ MATCHER = /^\d+$/.freeze
5
+
6
+ def transform?(value)
7
+ return false unless MATCHER.match(value)
8
+ return true, value.to_i
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,16 @@
1
+ module SafeYAML
2
+ class Transform
3
+ class ToNil
4
+ PREDEFINED_VALUES = {
5
+ "" => nil,
6
+ "~" => nil,
7
+ "null" => nil,
8
+ }.freeze
9
+
10
+ def transform?(value)
11
+ key = value.downcase
12
+ return PREDEFINED_VALUES.include?(key), PREDEFINED_VALUES[key]
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,12 @@
1
+ module SafeYAML
2
+ class Transform
3
+ class ToSymbol
4
+ MATCHER = /^:\w+$/.freeze
5
+
6
+ def transform?(value)
7
+ return false if !Transform::OPTIONS[:enable_symbol_parsing] || !MATCHER.match(value)
8
+ return true, value[1..-1].to_sym
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,15 @@
1
+ module SafeYAML
2
+ class Transform
3
+ class ToTime
4
+ # There isn't a missing '$' there; YAML itself seems to ignore everything at the end of a
5
+ # string that otherwise resembles a time.
6
+ MATCHER = /^\d{4}\-\d{2}\-\d{2} \d{2}:\d{2}:\d{2}(?:\.\d{1,5})?/.freeze
7
+
8
+ def transform?(value)
9
+ return false unless MATCHER.match(value)
10
+ datetime = DateTime.parse(value) rescue nil
11
+ return !!datetime, datetime.to_time
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,3 +1,3 @@
1
1
  module SafeYAML
2
- VERSION = "0.5"
2
+ VERSION = "0.5.1"
3
3
  end
@@ -1,6 +1,5 @@
1
1
  require File.join(File.dirname(__FILE__), "spec_helper")
2
2
 
3
- require "safe_yaml"
4
3
  require "exploitable_back_door"
5
4
 
6
5
  describe YAML do
data/spec/shared_specs.rb CHANGED
@@ -1,7 +1,5 @@
1
1
  require File.join(File.dirname(__FILE__), "spec_helper")
2
2
 
3
- require "safe_yaml/transform"
4
-
5
3
  module SharedSpecs
6
4
  def self.included(base)
7
5
  base.instance_eval do
@@ -53,6 +51,11 @@ module SharedSpecs
53
51
  result.should == { "date" => Date.parse("2013-01-24") }
54
52
  end
55
53
 
54
+ it "translates valid time values" do
55
+ parse "time: 2013-01-29 05:58:00 -0800"
56
+ result.should == { "time" => Time.new(2013, 1, 29, 5, 58, 0, "-08:00") }
57
+ end
58
+
56
59
  it "translates valid true/false values to booleans" do
57
60
  parse <<-YAML
58
61
  - yes
@@ -74,6 +77,30 @@ module SharedSpecs
74
77
  result.should == [nil] * 3
75
78
  end
76
79
 
80
+ it "deals just fine with nested maps" do
81
+ parse <<-YAML
82
+ foo:
83
+ bar:
84
+ marco: polo
85
+ YAML
86
+
87
+ result.should == { "foo" => { "bar" => { "marco" => "polo" } } }
88
+ end
89
+
90
+ it "deals just fine with nested sequences" do
91
+ parse <<-YAML
92
+ - foo
93
+ -
94
+ - bar1
95
+ - bar2
96
+ -
97
+ - baz1
98
+ - baz2
99
+ YAML
100
+
101
+ result.should == ["foo", ["bar1", "bar2", ["baz1", "baz2"]]]
102
+ end
103
+
77
104
  it "applies the same transformations to keys as to values" do
78
105
  parse <<-YAML
79
106
  foo: string
@@ -81,6 +108,7 @@ module SharedSpecs
81
108
  1: integer
82
109
  3.14: float
83
110
  2013-01-24: date
111
+ 2013-01-29 05:58:00 -0800: time
84
112
  YAML
85
113
 
86
114
  result.should == {
@@ -88,7 +116,8 @@ module SharedSpecs
88
116
  ":bar" => "symbol",
89
117
  1 => "integer",
90
118
  3.14 => "float",
91
- Date.parse("2013-01-24") => "date"
119
+ Date.parse("2013-01-24") => "date",
120
+ Time.new(2013, 1, 29, 5, 58, 0, "-08:00") => "time"
92
121
  }
93
122
  end
94
123
 
@@ -99,33 +128,10 @@ module SharedSpecs
99
128
  - 1
100
129
  - 3.14
101
130
  - 2013-01-24
131
+ - 2013-01-29 05:58:00 -0800
102
132
  YAML
103
133
 
104
- result.should == ["foo", ":bar", 1, 3.14, Date.parse("2013-01-24")]
105
- end
106
-
107
- it "deals just fine with nested maps" do
108
- parse <<-YAML
109
- foo:
110
- bar:
111
- marco: polo
112
- YAML
113
-
114
- result.should == { "foo" => { "bar" => { "marco" => "polo" } } }
115
- end
116
-
117
- it "deals just fine with nested sequences" do
118
- parse <<-YAML
119
- - foo
120
- -
121
- - bar1
122
- - bar2
123
- -
124
- - baz1
125
- - baz2
126
- YAML
127
-
128
- result.should == ["foo", ["bar1", "bar2", ["baz1", "baz2"]]]
134
+ result.should == ["foo", ":bar", 1, 3.14, Date.parse("2013-01-24"), Time.new(2013, 1, 29, 5, 58, 0, "-08:00")]
129
135
  end
130
136
  end
131
137
 
@@ -146,6 +152,7 @@ module SharedSpecs
146
152
  1: integer
147
153
  3.14: float
148
154
  2013-01-24: date
155
+ 2013-01-29 05:58:00 -0800: time
149
156
  YAML
150
157
 
151
158
  result.should == {
@@ -153,7 +160,8 @@ module SharedSpecs
153
160
  :bar => "symbol",
154
161
  1 => "integer",
155
162
  3.14 => "float",
156
- Date.parse("2013-01-24") => "date"
163
+ Date.parse("2013-01-24") => "date",
164
+ Time.new(2013, 1, 29, 5, 58, 0, "-08:00") => "time"
157
165
  }
158
166
  end
159
167
 
@@ -164,9 +172,10 @@ module SharedSpecs
164
172
  - 1
165
173
  - 3.14
166
174
  - 2013-01-24
175
+ - 2013-01-29 05:58:00 -0800
167
176
  YAML
168
177
 
169
- result.should == ["foo", :bar, 1, 3.14, Date.parse("2013-01-24")]
178
+ result.should == ["foo", :bar, 1, 3.14, Date.parse("2013-01-24"), Time.new(2013, 1, 29, 5, 58, 0, "-08:00")]
170
179
  end
171
180
  end
172
181
  end
data/spec/spec_helper.rb CHANGED
@@ -4,6 +4,7 @@ ROOT = File.join(HERE, "..")
4
4
  $LOAD_PATH << File.join(ROOT, "lib")
5
5
  $LOAD_PATH << File.join(HERE, "support")
6
6
 
7
+ require "safe_yaml"
7
8
  require "heredoc_unindent"
8
9
 
9
10
  require File.join(HERE, "shared_specs")
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.5'
4
+ version: 0.5.1
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-24 00:00:00.000000000 Z
12
+ date: 2013-01-29 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
@@ -26,6 +26,13 @@ files:
26
26
  - lib/safe_yaml/psych_handler.rb
27
27
  - lib/safe_yaml/syck_resolver.rb
28
28
  - lib/safe_yaml/transform.rb
29
+ - lib/safe_yaml/transform/to_boolean.rb
30
+ - lib/safe_yaml/transform/to_date.rb
31
+ - lib/safe_yaml/transform/to_float.rb
32
+ - lib/safe_yaml/transform/to_integer.rb
33
+ - lib/safe_yaml/transform/to_nil.rb
34
+ - lib/safe_yaml/transform/to_symbol.rb
35
+ - lib/safe_yaml/transform/to_time.rb
29
36
  - lib/safe_yaml/version.rb
30
37
  - run_specs_all_ruby_versions.sh
31
38
  - safe_yaml.gemspec