safe_yaml 0.6.3 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -99,14 +99,15 @@ The way that SafeYAML works is by restricting the kinds of objects that can be d
99
99
  - Booleans
100
100
  - Nils
101
101
 
102
- Additionally, deserialization of symbols can be enabled by calling `YAML.enable_symbol_parsing!`.
102
+ Additionally, deserialization of symbols can be enabled by calling `YAML.enable_symbol_parsing!` (for example, in an initializer).
103
103
 
104
104
  Known Issues
105
105
  ------------
106
106
 
107
107
  Also note that some Ruby libraries, particularly those requiring inter-process communication, leverage YAML's object deserialization functionality and therefore may break or otherwise be impacted by SafeYAML. The following list includes known instances of SafeYAML's interaction with other Ruby gems:
108
108
 
109
- - **Guard**: Uses YAML as a serialization format for notifications. The data serialized uses symbol keys, so calling `YAML.enable_symbol_parsing!` is necessary to allow Guard to work.
109
+ - [**Guard**](https://github.com/guard/guard): Uses YAML as a serialization format for notifications. The data serialized uses symbolic keys, so calling `YAML.enable_symbol_parsing!` is necessary to allow Guard to work.
110
+ - [**sidekiq**](https://github.com/mperham/sidekiq): Uses a YAML configiuration file with symbolic keys, so calling `YAML.enable_symbol_parsing!` should allow it to work.
110
111
 
111
112
  The above list will grow over time, as more issues are discovered.
112
113
 
@@ -119,3 +120,5 @@ Requirements
119
120
  ------------
120
121
 
121
122
  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).
123
+
124
+ If you are using a version of Ruby where Psych is the default YAML engine (e.g., 1.9.3) but you want to use Syck, be sure to set `YAML::ENGINE.yamler = "syck"` **before** requiring the safe_yaml gem.
data/lib/safe_yaml.rb CHANGED
@@ -10,6 +10,9 @@ require "safe_yaml/transform"
10
10
  require "safe_yaml/version"
11
11
 
12
12
  module SafeYAML
13
+ MULTI_ARGUMENT_YAML_LOAD = YAML.method(:load).arity != 1
14
+ YAML_ENGINE = defined?(YAML::ENGINE) ? YAML::ENGINE.yamler : "syck"
15
+
13
16
  OPTIONS = {
14
17
  :enable_symbol_parsing => false,
15
18
  :enable_arbitrary_object_deserialization => false,
@@ -18,14 +21,11 @@ module SafeYAML
18
21
  end
19
22
 
20
23
  module YAML
21
- def self.load_with_options(yaml, options={})
24
+ def self.load_with_options(yaml, *filename_and_options)
25
+ options = filename_and_options.last || {}
22
26
  safe_mode = safe_mode_from_options("load", options)
23
-
24
27
  arguments = [yaml]
25
- if RUBY_VERSION >= "1.9.3"
26
- arguments << options[:filename]
27
- end
28
-
28
+ arguments << filename_and_options.first if SafeYAML::MULTI_ARGUMENT_YAML_LOAD
29
29
  safe_mode ? safe_load(*arguments) : unsafe_load(*arguments)
30
30
  end
31
31
 
@@ -34,15 +34,16 @@ module YAML
34
34
  safe_mode ? safe_load_file(file) : unsafe_load_file(file)
35
35
  end
36
36
 
37
- def self.load_with_filename_and_options(yaml, filename=nil, options={})
38
- load_with_options(yaml, options.merge(:filename => filename))
39
- end
40
-
41
- if RUBY_VERSION >= "1.9.3"
37
+ if SafeYAML::YAML_ENGINE == "psych"
42
38
  require "safe_yaml/psych_handler"
43
39
  def self.safe_load(yaml, filename=nil)
44
40
  safe_handler = SafeYAML::PsychHandler.new
45
- Psych::Parser.new(safe_handler).parse(yaml, filename)
41
+ parser = Psych::Parser.new(safe_handler)
42
+ if SafeYAML::MULTI_ARGUMENT_YAML_LOAD
43
+ parser.parse(yaml, filename)
44
+ else
45
+ parser.parse(yaml)
46
+ end
46
47
  return safe_handler.result
47
48
  end
48
49
 
@@ -51,25 +52,13 @@ module YAML
51
52
  end
52
53
 
53
54
  def self.unsafe_load_file(filename)
54
- # https://github.com/tenderlove/psych/blob/v1.3.2/lib/psych.rb#L296-298
55
- File.open(filename, 'r:bom|utf-8') { |f| self.unsafe_load f, filename }
56
- end
57
-
58
- elsif RUBY_VERSION == "1.9.2"
59
- require "safe_yaml/psych_handler"
60
- def self.safe_load(yaml)
61
- safe_handler = SafeYAML::PsychHandler.new
62
- Psych::Parser.new(safe_handler).parse(yaml)
63
- return safe_handler.result
64
- end
65
-
66
- def self.safe_load_file(filename)
67
- File.open(filename, 'r:bom|utf-8') { |f| self.safe_load f }
68
- end
69
-
70
- def self.unsafe_load_file(filename)
71
- # https://github.com/tenderlove/psych/blob/v1.2.0/lib/psych.rb#L228-230
72
- File.open(filename, 'r:bom|utf-8') { |f| self.unsafe_load f }
55
+ if SafeYAML::MULTI_ARGUMENT_YAML_LOAD
56
+ # https://github.com/tenderlove/psych/blob/v1.3.2/lib/psych.rb#L296-298
57
+ File.open(filename, 'r:bom|utf-8') { |f| self.unsafe_load f, filename }
58
+ else
59
+ # https://github.com/tenderlove/psych/blob/v1.2.2/lib/psych.rb#L231-233
60
+ self.unsafe_load File.open(filename)
61
+ end
73
62
  end
74
63
 
75
64
  else
@@ -92,13 +81,7 @@ module YAML
92
81
 
93
82
  class << self
94
83
  alias_method :unsafe_load, :load
95
-
96
- if RUBY_VERSION >= "1.9.3"
97
- alias_method :load, :load_with_filename_and_options
98
- else
99
- alias_method :load, :load_with_options
100
- end
101
-
84
+ alias_method :load, :load_with_options
102
85
  alias_method :load_file, :load_file_with_options
103
86
 
104
87
  def enable_symbol_parsing?
@@ -124,18 +107,18 @@ module YAML
124
107
  def disable_arbitrary_object_deserialization!
125
108
  SafeYAML::OPTIONS[:enable_arbitrary_object_deserialization] = false
126
109
  end
127
- end
128
110
 
129
- private
130
- def self.safe_mode_from_options(method, options={})
131
- safe_mode = options[:safe]
111
+ private
112
+ def safe_mode_from_options(method, options={})
113
+ safe_mode = options[:safe]
132
114
 
133
- if safe_mode.nil?
134
- mode = SafeYAML::OPTIONS[:enable_arbitrary_object_deserialization] ? "unsafe" : "safe"
135
- Kernel.warn "Called '#{method}' without the :safe option -- defaulting to #{mode} mode." unless SafeYAML::OPTIONS[:suppress_warnings]
136
- safe_mode = !SafeYAML::OPTIONS[:enable_arbitrary_object_deserialization]
137
- end
115
+ if safe_mode.nil?
116
+ mode = SafeYAML::OPTIONS[:enable_arbitrary_object_deserialization] ? "unsafe" : "safe"
117
+ Kernel.warn "Called '#{method}' without the :safe option -- defaulting to #{mode} mode." unless SafeYAML::OPTIONS[:suppress_warnings]
118
+ safe_mode = !SafeYAML::OPTIONS[:enable_arbitrary_object_deserialization]
119
+ end
138
120
 
139
- safe_mode
121
+ safe_mode
122
+ end
140
123
  end
141
124
  end
@@ -1,4 +1,5 @@
1
1
  require "psych"
2
+ require "base64"
2
3
 
3
4
  module SafeYAML
4
5
  class PsychHandler < Psych::Handler
@@ -13,8 +14,8 @@ module SafeYAML
13
14
  @result
14
15
  end
15
16
 
16
- def add_to_current_structure(value, anchor=nil, quoted=nil)
17
- value = Transform.to_proper_type(value, quoted)
17
+ def add_to_current_structure(value, anchor=nil, quoted=nil, tag=nil)
18
+ value = Transform.to_proper_type(value, quoted, tag)
18
19
 
19
20
  @anchors[anchor] = value if anchor
20
21
 
@@ -62,7 +63,7 @@ module SafeYAML
62
63
  end
63
64
 
64
65
  def scalar(value, anchor, tag, plain, quoted, style)
65
- add_to_current_structure(value, anchor, quoted)
66
+ add_to_current_structure(value, anchor, quoted, tag)
66
67
  end
67
68
 
68
69
  def start_mapping(anchor, tag, implicit, style)
@@ -41,7 +41,7 @@ module SafeYAML
41
41
  end
42
42
 
43
43
  def resolve_scalar(node)
44
- Transform.to_proper_type(node.value, QUOTE_STYLES.include?(node.instance_variable_get(:@style)))
44
+ Transform.to_proper_type(node.value, QUOTE_STYLES.include?(node.instance_variable_get(:@style)), node.type_id)
45
45
  end
46
46
  end
47
47
  end
@@ -1,3 +1,5 @@
1
+ require 'base64'
2
+
1
3
  module SafeYAML
2
4
  class Transform
3
5
  TRANSFORMERS = [
@@ -10,7 +12,7 @@ module SafeYAML
10
12
  Transform::ToTime.new
11
13
  ]
12
14
 
13
- def self.to_proper_type(value, quoted=false)
15
+ def self.to_guessed_type(value, quoted=false)
14
16
  return value if quoted
15
17
 
16
18
  if value.is_a?(String)
@@ -22,5 +24,16 @@ module SafeYAML
22
24
 
23
25
  value
24
26
  end
27
+
28
+ def self.to_proper_type(value, quoted=false,tag=nil)
29
+ case tag
30
+ when "tag:yaml.org,2002:binary"
31
+ return Base64.decode64(value)
32
+ when "x-private:binary"
33
+ return Base64.decode64(value)
34
+ else
35
+ return self.to_guessed_type(value, quoted)
36
+ end
37
+ end
25
38
  end
26
39
  end
@@ -1,3 +1,3 @@
1
1
  module SafeYAML
2
- VERSION = "0.6.3"
2
+ VERSION = "0.7.0"
3
3
  end
@@ -6,10 +6,16 @@ rvm use 1.8.7@safe_yaml
6
6
  rake spec
7
7
 
8
8
  rvm use 1.9.2@safe_yaml
9
- rake spec
9
+ YAMLER=syck rake spec
10
10
 
11
11
  rvm use 1.9.3@safe_yaml
12
- rake spec
12
+ YAMLER=syck rake spec
13
+
14
+ rvm use 1.9.2@safe_yaml
15
+ YAMLER=psych rake spec
16
+
17
+ rvm use 1.9.3@safe_yaml
18
+ YAMLER=psych rake spec
13
19
 
14
20
  rvm use 2.0.0@safe_yaml
15
- rake spec
21
+ YAMLER=psych rake spec
@@ -1,6 +1,6 @@
1
1
  require File.join(File.dirname(__FILE__), "spec_helper")
2
2
 
3
- if RUBY_VERSION >= "1.9.2"
3
+ if SafeYAML::YAML_ENGINE == "psych"
4
4
  require "safe_yaml/psych_handler"
5
5
 
6
6
  describe SafeYAML::PsychHandler do
@@ -16,7 +16,7 @@ describe YAML do
16
16
  end
17
17
 
18
18
  describe "unsafe_load" do
19
- if RUBY_VERSION >= "1.9.3"
19
+ if SafeYAML::YAML_ENGINE == "psych" && RUBY_VERSION >= "1.9.3"
20
20
  it "allows exploits through objects defined in YAML w/ !ruby/hash via custom :[]= methods" do
21
21
  backdoor = YAML.unsafe_load("--- !ruby/hash:ExploitableBackDoor\nfoo: bar\n")
22
22
  backdoor.should be_exploited_through_setter
@@ -45,6 +45,33 @@ describe YAML do
45
45
  object.should_not be_a(ExploitableBackDoor)
46
46
  end
47
47
 
48
+ context "for YAML engine #{SafeYAML::YAML_ENGINE}" do
49
+ if SafeYAML::YAML_ENGINE == "psych"
50
+ let(:arguments) {
51
+ if SafeYAML::MULTI_ARGUMENT_YAML_LOAD
52
+ ["foo: bar", nil]
53
+ else
54
+ ["foo: bar"]
55
+ end
56
+ }
57
+ it "uses Psych internally to parse YAML" do
58
+ stub_parser = stub(Psych::Parser)
59
+ Psych::Parser.stub(:new).and_return(stub_parser)
60
+ stub_parser.should_receive(:parse).with(*arguments)
61
+ # This won't work now; we just want to ensure Psych::Parser#parse was in fact called.
62
+ YAML.safe_load(*arguments)
63
+ end
64
+ end
65
+
66
+ if SafeYAML::YAML_ENGINE == "syck"
67
+ it "uses Syck internally to parse YAML" do
68
+ YAML.should_receive(:parse).with("foo: bar")
69
+ # This won't work now; we just want to ensure YAML::parse was in fact called.
70
+ YAML.safe_load("foo: bar") rescue nil
71
+ end
72
+ end
73
+ end
74
+
48
75
  it "loads a plain ol' YAML document just fine" do
49
76
  result = YAML.safe_load <<-YAML.unindent
50
77
  foo:
@@ -76,6 +103,30 @@ describe YAML do
76
103
  result.should == [{}, {}, {}]
77
104
  end
78
105
 
106
+ it "works for YAML documents with binary tagged keys" do
107
+ result = YAML.safe_load <<-YAML
108
+ ? !!binary >
109
+ Zm9v
110
+ : "bar"
111
+ ? !!binary >
112
+ YmFy
113
+ : "baz"
114
+ YAML
115
+
116
+ result.should == {"foo" => "bar", "bar" => "baz"}
117
+ end
118
+
119
+ it "works for YAML documents with binary tagged values" do
120
+ result = YAML.safe_load <<-YAML
121
+ "foo": !!binary >
122
+ YmFy
123
+ "bar": !!binary >
124
+ YmF6
125
+ YAML
126
+
127
+ result.should == {"foo" => "bar", "bar" => "baz"}
128
+ end
129
+
79
130
  it "works for YAML documents with sections" do
80
131
  result = YAML.safe_load <<-YAML
81
132
  mysql: &mysql
@@ -134,12 +185,14 @@ describe YAML do
134
185
  end
135
186
 
136
187
  describe "unsafe_load_file" do
137
- if RUBY_VERSION >= "1.9.3"
188
+ if SafeYAML::YAML_ENGINE == "psych" && RUBY_VERSION >= "1.9.3"
138
189
  it "allows exploits through objects defined in YAML w/ !ruby/hash via custom :[]= methods" do
139
190
  backdoor = YAML.unsafe_load_file "spec/exploit.1.9.3.yaml"
140
191
  backdoor.should be_exploited_through_setter
141
192
  end
193
+ end
142
194
 
195
+ if SafeYAML::YAML_ENGINE == "psych" && RUBY_VERSION >= "1.9.2"
143
196
  it "allows exploits through objects defined in YAML w/ !ruby/object via the :init_with method" do
144
197
  backdoor = YAML.unsafe_load_file "spec/exploit.1.9.2.yaml"
145
198
  backdoor.should be_exploited_through_init_with
@@ -166,7 +219,7 @@ describe YAML do
166
219
 
167
220
  describe "load" do
168
221
  let (:arguments) {
169
- if RUBY_VERSION >= "1.9.3"
222
+ if SafeYAML::MULTI_ARGUMENT_YAML_LOAD
170
223
  ["foo: bar", nil]
171
224
  else
172
225
  ["foo: bar"]
data/spec/shared_specs.rb CHANGED
@@ -161,6 +161,7 @@ module SharedSpecs
161
161
  end
162
162
  end
163
163
 
164
+ # This does in fact appear to be a Ruby version thing as opposed to a YAML engine thing.
164
165
  context "for Ruby version #{RUBY_VERSION}" do
165
166
  if RUBY_VERSION >= "1.9.2"
166
167
  it "translates valid time values" do
data/spec/spec_helper.rb CHANGED
@@ -4,6 +4,12 @@ ROOT = File.join(HERE, "..")
4
4
  $LOAD_PATH << File.join(ROOT, "lib")
5
5
  $LOAD_PATH << File.join(HERE, "support")
6
6
 
7
+ if ENV["YAMLER"]
8
+ require "yaml"
9
+ YAML::ENGINE.yamler = ENV["YAMLER"]
10
+ puts "Running specs in Ruby #{RUBY_VERSION} with '#{YAML::ENGINE.yamler}' YAML engine."
11
+ end
12
+
7
13
  require "safe_yaml"
8
14
  require "heredoc_unindent"
9
15
 
@@ -1,6 +1,6 @@
1
1
  require File.join(File.dirname(__FILE__), "spec_helper")
2
2
 
3
- if RUBY_VERSION < "1.9.2"
3
+ if SafeYAML::YAML_ENGINE == "syck"
4
4
  require "safe_yaml/syck_resolver"
5
5
 
6
6
  describe SafeYAML::SyckResolver do
@@ -1,7 +1,7 @@
1
1
  require File.join(File.dirname(__FILE__), "..", "spec_helper")
2
2
 
3
3
  describe SafeYAML::Transform::ToTime do
4
- # YAML don't parse times prior to 1.9.
4
+ # It seems both Psych and Syck parse times starting w/ Ruby 1.9.2.
5
5
  if RUBY_VERSION >= "1.9.2"
6
6
  it "returns true when the value matches a valid Time" do
7
7
  subject.transform?("2013-01-01 10:00:00")[0].should == true
metadata CHANGED
@@ -1,23 +1,32 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: safe_yaml
3
- version: !ruby/object:Gem::Version
4
- version: 0.6.3
3
+ version: !ruby/object:Gem::Version
4
+ hash: 3
5
5
  prerelease:
6
+ segments:
7
+ - 0
8
+ - 7
9
+ - 0
10
+ version: 0.7.0
6
11
  platform: ruby
7
- authors:
12
+ authors:
8
13
  - Dan Tao
9
14
  autorequire:
10
15
  bindir: bin
11
16
  cert_chain: []
12
- date: 2013-02-06 00:00:00.000000000 Z
17
+
18
+ date: 2013-02-08 00:00:00 Z
13
19
  dependencies: []
14
- description: Parse YAML safely, without that pesky arbitrary object deserialization
15
- vulnerability
20
+
21
+ description: Parse YAML safely, without that pesky arbitrary object deserialization vulnerability
16
22
  email: daniel.tao@gmail.com
17
23
  executables: []
24
+
18
25
  extensions: []
26
+
19
27
  extra_rdoc_files: []
20
- files:
28
+
29
+ files:
21
30
  - .gitignore
22
31
  - .travis.yml
23
32
  - Gemfile
@@ -52,32 +61,41 @@ files:
52
61
  - spec/transform/to_symbol_spec.rb
53
62
  - spec/transform/to_time_spec.rb
54
63
  homepage: http://dtao.github.com/safe_yaml/
55
- licenses:
64
+ licenses:
56
65
  - MIT
57
66
  post_install_message:
58
67
  rdoc_options: []
59
- require_paths:
68
+
69
+ require_paths:
60
70
  - lib
61
- required_ruby_version: !ruby/object:Gem::Requirement
71
+ required_ruby_version: !ruby/object:Gem::Requirement
62
72
  none: false
63
- requirements:
64
- - - ! '>='
65
- - !ruby/object:Gem::Version
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ hash: 57
77
+ segments:
78
+ - 1
79
+ - 8
80
+ - 7
66
81
  version: 1.8.7
67
- required_rubygems_version: !ruby/object:Gem::Requirement
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
83
  none: false
69
- requirements:
70
- - - ! '>='
71
- - !ruby/object:Gem::Version
72
- version: '0'
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ hash: 3
88
+ segments:
89
+ - 0
90
+ version: "0"
73
91
  requirements: []
92
+
74
93
  rubyforge_project:
75
- rubygems_version: 1.8.24
94
+ rubygems_version: 1.8.25
76
95
  signing_key:
77
96
  specification_version: 3
78
- summary: SameYAML provides an alternative implementation of YAML.load suitable for
79
- accepting user input in Ruby applications.
80
- test_files:
97
+ summary: SameYAML provides an alternative implementation of YAML.load suitable for accepting user input in Ruby applications.
98
+ test_files:
81
99
  - spec/exploit.1.9.2.yaml
82
100
  - spec/exploit.1.9.3.yaml
83
101
  - spec/psych_handler_spec.rb