safe_yaml 0.1

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 ADDED
@@ -0,0 +1,9 @@
1
+ source :rubygems
2
+
3
+ gemspec
4
+
5
+ group :development do
6
+ gem "heredoc_unindent"
7
+ gem "rake"
8
+ gem "rspec"
9
+ end
@@ -0,0 +1,28 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ safe_yaml (0.1)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.1.3)
10
+ heredoc_unindent (1.1.2)
11
+ rake (10.0.3)
12
+ rspec (2.12.0)
13
+ rspec-core (~> 2.12.0)
14
+ rspec-expectations (~> 2.12.0)
15
+ rspec-mocks (~> 2.12.0)
16
+ rspec-core (2.12.2)
17
+ rspec-expectations (2.12.1)
18
+ diff-lcs (~> 1.1.3)
19
+ rspec-mocks (2.12.1)
20
+
21
+ PLATFORMS
22
+ ruby
23
+
24
+ DEPENDENCIES
25
+ heredoc_unindent
26
+ rake
27
+ rspec
28
+ safe_yaml!
@@ -0,0 +1,6 @@
1
+ require "rspec/core/rake_task"
2
+
3
+ desc "Run specs"
4
+ RSpec::Core::RakeTask.new(:spec) do |t|
5
+ t.rspec_opts = %w(--color --format d)
6
+ end
@@ -0,0 +1,86 @@
1
+ require "yaml"
2
+
3
+ module SafeYAML
4
+ class Handler < Psych::Handler
5
+ def initialize
6
+ @stack = []
7
+ end
8
+
9
+ def result
10
+ @result
11
+ end
12
+
13
+ def add_to_current_structure(value)
14
+ if @result.nil?
15
+ @result = value
16
+ @current_structure = @result
17
+ return
18
+ end
19
+
20
+ case @current_structure
21
+ when Array
22
+ @current_structure.push(transform_value(value))
23
+
24
+ when Hash
25
+ if @current_key.nil?
26
+ @current_key = transform_value(value)
27
+ else
28
+ @current_structure[@current_key] = transform_value(value)
29
+ @current_key = nil
30
+ end
31
+
32
+ else
33
+ raise "Don't know how to add to a #{@current_structure.class}!"
34
+ end
35
+ end
36
+
37
+ def transform_value(value)
38
+ if value.is_a?(String)
39
+ if value.match(/^:\w+$/)
40
+ return value[1..-1].to_sym
41
+
42
+ elsif value.match(/^\d+$/)
43
+ return value.to_i
44
+
45
+ elsif value.match(/^\d+(?:\.\d*)?$/) || value.match(/^\.\d+$/)
46
+ return value.to_f
47
+ end
48
+ end
49
+
50
+ value
51
+ end
52
+
53
+ def streaming?
54
+ false
55
+ end
56
+
57
+ # event handlers
58
+ def scalar(value, anchor, tag, plain, quoted, style)
59
+ add_to_current_structure(value)
60
+ end
61
+
62
+ def start_mapping(*args) # anchor, tag, implicit, style
63
+ map = {}
64
+ self.add_to_current_structure(map)
65
+ @current_structure = map
66
+ @stack.push(map)
67
+ end
68
+
69
+ def end_mapping
70
+ @stack.pop
71
+ @current_structure = @stack.last
72
+ end
73
+
74
+ def start_sequence(*args) # anchor, tag, implicit, style
75
+ seq = []
76
+ self.add_to_current_structure(seq)
77
+ @current_structure = seq
78
+ @stack.push(seq)
79
+ end
80
+
81
+ def end_sequence
82
+ @stack.pop
83
+ @current_structure = @stack.last
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,10 @@
1
+ require "yaml"
2
+ require "handler"
3
+
4
+ module YAML
5
+ def self.safe_load(yaml)
6
+ safe_handler = SafeYAML::Handler.new
7
+ Psych::Parser.new(safe_handler).parse(yaml)
8
+ return safe_handler.result
9
+ end
10
+ end
@@ -0,0 +1,3 @@
1
+ module SafeYAML
2
+ VERSION = "0.1"
3
+ end
@@ -0,0 +1,16 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path("../lib/version", __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.name = "safe_yaml"
6
+ gem.authors = ["Dan Tao"]
7
+ gem.email = ["daniel.tao@gmail.com"]
8
+ gem.description = %q{Parse (simple) YAML safely, without that pesky arbitrary code execution vulnerability.}
9
+ 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.homepage = "http://dtao.github.com/safe_yaml/"
11
+
12
+ gem.files = `git ls-files`.split($\)
13
+ gem.test_files = gem.files.grep(%r{^spec/})
14
+ gem.require_paths = ["lib"]
15
+ gem.version = SafeYAML::VERSION
16
+ end
@@ -0,0 +1,108 @@
1
+ require File.join(File.dirname(__FILE__), "spec_helper")
2
+
3
+ require "handler"
4
+
5
+ describe SafeYAML::Handler do
6
+ let(:handler) { SafeYAML::Handler.new }
7
+ let(:parser) { Psych::Parser.new(handler) }
8
+ let(:result) { handler.result }
9
+
10
+ def parse(yaml)
11
+ parser.parse(yaml.unindent)
12
+ end
13
+
14
+ it "translates most values to strings" do
15
+ parser.parse "key: value"
16
+ result.should == { "key" => "value" }
17
+ end
18
+
19
+ it "translates values starting with ':' to symbols" do
20
+ parser.parse ":key: value"
21
+ result.should == { :key => "value" }
22
+ end
23
+
24
+ it "translates valid integral numbers to integers" do
25
+ parser.parse "integer: 1"
26
+ result.should == { "integer" => 1 }
27
+ end
28
+
29
+ it "translates valid decimal numbers to floats" do
30
+ parser.parse "float: 3.14"
31
+ result.should == { "float" => 3.14 }
32
+ end
33
+
34
+ it "applies the same transformations to values as to keys" do
35
+ parse <<-YAML
36
+ string: value
37
+ symbol: :value
38
+ integer: 1
39
+ float: 3.14
40
+ YAML
41
+
42
+ result.should == {
43
+ "string" => "value",
44
+ "symbol" => :value,
45
+ "integer" => 1,
46
+ "float" => 3.14
47
+ }
48
+ end
49
+
50
+ it "translates sequences to arrays" do
51
+ parse <<-YAML
52
+ - foo
53
+ - bar
54
+ - baz
55
+ YAML
56
+
57
+ result.should == ["foo", "bar", "baz"]
58
+ end
59
+
60
+ it "applies the same transformations to elements in sequences as to all values" do
61
+ parse <<-YAML
62
+ - string
63
+ - :symbol
64
+ - 1
65
+ - 3.14
66
+ YAML
67
+
68
+ result.should == ["string", :symbol, 1, 3.14]
69
+ end
70
+
71
+ it "translates maps to hashes" do
72
+ parse <<-YAML
73
+ foo: blah
74
+ bar: glah
75
+ baz: flah
76
+ YAML
77
+
78
+ result.should == {
79
+ "foo" => "blah",
80
+ "bar" => "glah",
81
+ "baz" => "flah"
82
+ }
83
+ end
84
+
85
+ it "applies the same transformations to values in hashes as to all values" do
86
+ parse <<-YAML
87
+ foo: :symbol
88
+ bar: 1
89
+ baz: 3.14
90
+ YAML
91
+
92
+ result.should == {
93
+ "foo" => :symbol,
94
+ "bar" => 1,
95
+ "baz" => 3.14
96
+ }
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
+ end
@@ -0,0 +1,57 @@
1
+ require File.join(File.dirname(__FILE__), "spec_helper")
2
+
3
+ require "safe_yaml"
4
+ require "exploitable_back_door"
5
+
6
+ describe YAML do
7
+ before :each do
8
+ ExploitableBackDoor.reset
9
+ end
10
+
11
+ describe "load" do
12
+ if RUBY_VERSION >= "1.9.3"
13
+ it "allows exploits through objects defined in YAML w/ !ruby/hash" do
14
+ YAML.load "--- !ruby/hash:ExploitableBackDoor\nfoo: bar\n"
15
+ ExploitableBackDoor.should be_exploited
16
+ end
17
+ end
18
+
19
+ it "allows exploits through objects defined in YAML w/ !ruby/object" do
20
+ YAML.load "--- !ruby/object:ExploitableBackDoor\nfoo: bar\n"
21
+ ExploitableBackDoor.should be_exploited
22
+ end
23
+ end
24
+
25
+ describe "safe_load" do
26
+ it "does NOT allow exploits through objects defined in YAML w/ !ruby/object" do
27
+ YAML.safe_load "--- !ruby/object:ExploitableBackDoor\nfoo: bar\n"
28
+ ExploitableBackDoor.should_not be_exploited
29
+ end
30
+
31
+ it "does NOT allow exploits through objects defined in YAML w/ !ruby/hash" do
32
+ YAML.safe_load "--- !ruby/hash:ExploitableBackDoor\nfoo: bar\n"
33
+ ExploitableBackDoor.should_not be_exploited
34
+ end
35
+
36
+ it "loads a plain ol' YAML document just fine" do
37
+ result = YAML.safe_load <<-YAML.unindent
38
+ foo:
39
+ number: 1
40
+ string: Hello, there!
41
+ symbol: :blah
42
+ sequence:
43
+ - hi
44
+ - bye
45
+ YAML
46
+
47
+ result.should == {
48
+ "foo" => {
49
+ "number" => 1,
50
+ "string" => "Hello, there!",
51
+ "symbol" => :blah,
52
+ "sequence" => ["hi", "bye"]
53
+ }
54
+ }
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,7 @@
1
+ HERE = File.dirname(__FILE__)
2
+ ROOT = File.join(HERE, "..")
3
+
4
+ $LOAD_PATH << File.join(ROOT, "lib")
5
+ $LOAD_PATH << File.join(HERE, "support")
6
+
7
+ require "heredoc_unindent"
@@ -0,0 +1,23 @@
1
+ class ExploitableBackDoor
2
+ @@exploited = false
3
+
4
+ def self.exploited?
5
+ @@exploited
6
+ end
7
+
8
+ def self.reset
9
+ @@exploited = false
10
+ end
11
+
12
+ def init_with(command)
13
+ # Note: this is how bad this COULD be.
14
+ # system("#{command}")
15
+ @@exploited = true
16
+ end
17
+
18
+ def []=(command, arguments)
19
+ # Note: this is how bad this COULD be.
20
+ # system("#{command} #{arguments}")
21
+ @@exploited = true
22
+ end
23
+ end
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: safe_yaml
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Dan Tao
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-01-17 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Parse (simple) YAML safely, without that pesky arbitrary code execution
15
+ vulnerability.
16
+ email:
17
+ - daniel.tao@gmail.com
18
+ executables: []
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - Gemfile
23
+ - Gemfile.lock
24
+ - Rakefile
25
+ - lib/handler.rb
26
+ - lib/safe_yaml.rb
27
+ - lib/version.rb
28
+ - safe_yaml.gemspec
29
+ - spec/handler_spec.rb
30
+ - spec/safe_yaml_spec.rb
31
+ - spec/spec_helper.rb
32
+ - spec/support/exploitable_back_door.rb
33
+ homepage: http://dtao.github.com/safe_yaml/
34
+ licenses: []
35
+ post_install_message:
36
+ rdoc_options: []
37
+ require_paths:
38
+ - lib
39
+ required_ruby_version: !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ required_rubygems_version: !ruby/object:Gem::Requirement
46
+ none: false
47
+ requirements:
48
+ - - ! '>='
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ requirements: []
52
+ rubyforge_project:
53
+ rubygems_version: 1.8.24
54
+ signing_key:
55
+ specification_version: 3
56
+ summary: SameYAML adds a ::safe_load method to Ruby's built-in YAML module to parse
57
+ YAML data for only basic types (strings, symbols, numbers, arrays, and hashes).
58
+ test_files:
59
+ - spec/handler_spec.rb
60
+ - spec/safe_yaml_spec.rb
61
+ - spec/spec_helper.rb
62
+ - spec/support/exploitable_back_door.rb