puppet 2.7.21 → 2.7.22
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of puppet might be problematic. Click here for more details.
- data/CHANGELOG +14 -0
- data/Gemfile.lock +2 -2
- data/ext/build_defaults.yaml +2 -3
- data/ext/debian/control +1 -1
- data/ext/packaging/README.md +496 -8
- data/ext/packaging/spec/tasks/00_utils_spec.rb +7 -7
- data/ext/packaging/spec/tasks/build_object_spec.rb +3 -0
- data/ext/packaging/tasks/00_utils.rake +2 -2
- data/ext/packaging/tasks/10_setupvars.rake +8 -1
- data/ext/packaging/tasks/build.rake +2 -0
- data/ext/packaging/tasks/deb_repos.rake +48 -15
- data/ext/packaging/tasks/jenkins.rake +30 -2
- data/ext/packaging/tasks/mock.rake +3 -2
- data/ext/packaging/tasks/pe_remote.rake +1 -1
- data/ext/packaging/tasks/pe_ship.rake +4 -5
- data/ext/packaging/tasks/pe_sign.rake +8 -0
- data/ext/packaging/tasks/pe_sles.rake +8 -7
- data/ext/packaging/tasks/pre_tasks.rake +0 -0
- data/ext/packaging/tasks/retrieve.rake +11 -1
- data/ext/packaging/tasks/rpm_repos.rake +71 -49
- data/ext/packaging/tasks/ship.rake +14 -2
- data/ext/packaging/tasks/sign.rake +9 -3
- data/ext/packaging/tasks/tar.rake +5 -0
- data/ext/packaging/tasks/vendor_gems.rake +110 -0
- data/install.rb +1 -1
- data/lib/puppet.rb +11 -0
- data/lib/puppet/indirector/report/processor.rb +1 -1
- data/lib/puppet/indirector/report/rest.rb +7 -0
- data/lib/puppet/indirector/resource/rest.rb +9 -0
- data/lib/puppet/indirector/rest.rb +81 -47
- data/lib/puppet/indirector/run/rest.rb +6 -0
- data/lib/puppet/network/formats.rb +20 -10
- data/lib/puppet/network/http/handler.rb +1 -1
- data/lib/puppet/node.rb +25 -0
- data/lib/puppet/node/facts.rb +23 -4
- data/lib/puppet/resource.rb +2 -4
- data/lib/puppet/resource/status.rb +28 -0
- data/lib/puppet/run.rb +24 -2
- data/lib/puppet/status.rb +6 -2
- data/lib/puppet/transaction/event.rb +19 -0
- data/lib/puppet/transaction/report.rb +39 -0
- data/lib/puppet/util/log.rb +19 -0
- data/lib/puppet/util/metric.rb +6 -0
- data/lib/puppet/util/monkey_patches.rb +0 -16
- data/lib/puppet/vendor.rb +55 -0
- data/lib/puppet/vendor/load_safe_yaml.rb +1 -0
- data/lib/puppet/vendor/require_vendored.rb +4 -0
- data/lib/puppet/vendor/safe_yaml/CHANGES.md +104 -0
- data/lib/puppet/vendor/safe_yaml/Gemfile +11 -0
- data/lib/puppet/vendor/safe_yaml/LICENSE.txt +22 -0
- data/lib/puppet/vendor/safe_yaml/README.md +179 -0
- data/lib/puppet/vendor/safe_yaml/Rakefile +6 -0
- data/lib/puppet/vendor/safe_yaml/lib/safe_yaml.rb +253 -0
- data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/deep.rb +34 -0
- data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/parse/date.rb +27 -0
- data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/parse/hexadecimal.rb +12 -0
- data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/parse/sexagesimal.rb +26 -0
- data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/psych_handler.rb +92 -0
- data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/psych_resolver.rb +52 -0
- data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/resolver.rb +94 -0
- data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/safe_to_ruby_visitor.rb +17 -0
- data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/syck_hack.rb +36 -0
- data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/syck_node_monkeypatch.rb +43 -0
- data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/syck_resolver.rb +38 -0
- data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/transform.rb +41 -0
- data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/transform/to_boolean.rb +21 -0
- data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/transform/to_date.rb +11 -0
- data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/transform/to_float.rb +33 -0
- data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/transform/to_integer.rb +25 -0
- data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/transform/to_nil.rb +18 -0
- data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/transform/to_symbol.rb +13 -0
- data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/transform/transformation_map.rb +47 -0
- data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/version.rb +3 -0
- data/lib/puppet/vendor/safe_yaml/run_specs_all_ruby_versions.sh +21 -0
- data/lib/puppet/vendor/safe_yaml/safe_yaml.gemspec +18 -0
- data/lib/puppet/vendor/safe_yaml/spec/exploit.1.9.2.yaml +2 -0
- data/lib/puppet/vendor/safe_yaml/spec/exploit.1.9.3.yaml +2 -0
- data/lib/puppet/vendor/safe_yaml/spec/psych_resolver_spec.rb +10 -0
- data/lib/puppet/vendor/safe_yaml/spec/resolver_specs.rb +250 -0
- data/lib/puppet/vendor/safe_yaml/spec/safe_yaml_spec.rb +702 -0
- data/lib/puppet/vendor/safe_yaml/spec/spec_helper.rb +18 -0
- data/lib/puppet/vendor/safe_yaml/spec/support/exploitable_back_door.rb +29 -0
- data/lib/puppet/vendor/safe_yaml/spec/syck_resolver_spec.rb +10 -0
- data/lib/puppet/vendor/safe_yaml/spec/transform/base64_spec.rb +11 -0
- data/lib/puppet/vendor/safe_yaml/spec/transform/to_date_spec.rb +34 -0
- data/lib/puppet/vendor/safe_yaml/spec/transform/to_float_spec.rb +42 -0
- data/lib/puppet/vendor/safe_yaml/spec/transform/to_integer_spec.rb +59 -0
- data/lib/puppet/vendor/safe_yaml/spec/transform/to_symbol_spec.rb +49 -0
- data/lib/puppet/version.rb +1 -1
- data/spec/lib/puppet_spec/matchers.rb +8 -0
- data/spec/unit/file_serving/metadata_spec.rb +20 -28
- data/spec/unit/indirector/report/rest_spec.rb +41 -0
- data/spec/unit/indirector/rest_spec.rb +314 -339
- data/spec/unit/network/formats_spec.rb +36 -27
- data/spec/unit/network/http/handler_spec.rb +3 -12
- data/spec/unit/node_spec.rb +81 -0
- data/spec/unit/resource_spec.rb +5 -35
- data/spec/unit/run_spec.rb +22 -8
- data/spec/unit/status_spec.rb +6 -0
- data/test/network/handler/report.rb +0 -36
- metadata +148 -102
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Dan Tao
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,179 @@
|
|
1
|
+
SafeYAML
|
2
|
+
========
|
3
|
+
|
4
|
+
[![Build Status](https://travis-ci.org/dtao/safe_yaml.png)](http://travis-ci.org/dtao/safe_yaml)
|
5
|
+
|
6
|
+
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 ones discovered](http://www.reddit.com/r/netsec/comments/167c11/serious_vulnerability_in_ruby_on_rails_allowing/) [in Rails in early 2013](http://www.h-online.com/open/news/item/Rails-developers-close-another-extremely-critical-flaw-1793511.html)).
|
7
|
+
|
8
|
+
**If you encounter any issues with SafeYAML, check out the 'Common Issues' section below.** If you don't see anything that addresses the problem you're experiencing, by all means, [create an issue](https://github.com/dtao/safe_yaml/issues/new)!
|
9
|
+
|
10
|
+
Installation
|
11
|
+
------------
|
12
|
+
|
13
|
+
Add this line to your application's Gemfile:
|
14
|
+
|
15
|
+
gem "safe_yaml"
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
$ gem install safe_yaml
|
24
|
+
|
25
|
+
Configuration
|
26
|
+
-------------
|
27
|
+
|
28
|
+
Configuring SafeYAML should be quick. In most cases, you will probably only have to think about two things:
|
29
|
+
|
30
|
+
1. What do you want the `YAML` module's *default* behavior to be? Set the `SafeYAML::OPTIONS[:default_mode]` option to either `:safe` or `:unsafe` to control this. If you do neither, SafeYAML will default to `:safe` mode but will issue a warning the first time you call `YAML.load`.
|
31
|
+
2. Do you want to allow symbols by default? Set the `SafeYAML::OPTIONS[:deserialize_symbols]` option to `true` or `false` to control this. The default is `false`, which means that SafeYAML will deserialize symbols in YAML documents as strings.
|
32
|
+
|
33
|
+
For more information on these and other options, see the "Usage" section down below.
|
34
|
+
|
35
|
+
Explanation
|
36
|
+
-----------
|
37
|
+
|
38
|
+
Suppose your application were to use a popular open source library which contained code like this:
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
class ClassBuilder
|
42
|
+
def []=(key, value)
|
43
|
+
@class ||= Class.new
|
44
|
+
|
45
|
+
@class.class_eval <<-EOS
|
46
|
+
def #{key}
|
47
|
+
#{value}
|
48
|
+
end
|
49
|
+
EOS
|
50
|
+
end
|
51
|
+
|
52
|
+
def create
|
53
|
+
@class.new
|
54
|
+
end
|
55
|
+
end
|
56
|
+
```
|
57
|
+
|
58
|
+
Now, if you were to use `YAML.load` on user input anywhere in your application without the SafeYAML gem installed, an attacker who suspected you were using this library could send a request with a carefully-crafted YAML string to execute arbitrary code (yes, including `system("unix command")`) on your servers.
|
59
|
+
|
60
|
+
This simple example demonstrates the vulnerability:
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
yaml = <<-EOYAML
|
64
|
+
--- !ruby/hash:ClassBuilder
|
65
|
+
"foo; end; puts %(I'm in yr system!); def bar": "baz"
|
66
|
+
EOYAML
|
67
|
+
```
|
68
|
+
|
69
|
+
> YAML.load(yaml)
|
70
|
+
I'm in yr system!
|
71
|
+
=> #<ClassBuilder:0x007fdbbe2e25d8 @class=#<Class:0x007fdbbe2e2510>>
|
72
|
+
|
73
|
+
With SafeYAML, the same attacker would be thwarted:
|
74
|
+
|
75
|
+
> require "safe_yaml"
|
76
|
+
=> true
|
77
|
+
> YAML.load(yaml, :safe => true)
|
78
|
+
=> {"foo; end; puts %(I'm in yr system!); def bar"=>"baz"}
|
79
|
+
|
80
|
+
Usage
|
81
|
+
-----
|
82
|
+
|
83
|
+
When you require the safe_yaml gem in your project, `YAML.load` is patched to accept one additional (optional) `options` parameter. This changes the method signature as follows:
|
84
|
+
|
85
|
+
- for Syck and Psych prior to Ruby 1.9.3: `YAML.load(yaml, options={})`
|
86
|
+
- for Psych in 1.9.3 and later: `YAML.load(yaml, filename=nil, options={})`
|
87
|
+
|
88
|
+
The most important option is the `:safe` option (default: `true`), which controls whether or not to deserialize arbitrary objects when parsing a YAML document. The other options, along with explanations, are as follows.
|
89
|
+
|
90
|
+
- `:deserialize_symbols` (default: `false`): Controls whether or not YAML will deserialize symbols. It is probably best to only enable this option where necessary, e.g. to make trusted libraries work. Symbols receive special treatment in Ruby and are not garbage collected, which means deserializing them indiscriminately may render your site vulnerable to a DOS attack (hence `false` as a default value).
|
91
|
+
|
92
|
+
- `:whitelisted_tags`: Accepts an array of YAML tags that designate trusted types, e.g., ones that can be deserialized without worrying about any resulting security vulnerabilities. When any of the given tags are encountered in a YAML document, the associated data will be parsed by the underlying YAML engine (Syck or Psych) for the version of Ruby you are using. See the "Whitelisting Trusted Types" section below for more information.
|
93
|
+
|
94
|
+
- `:custom_initializers`: Similar to the `:whitelisted_tags` option, but allows you to provide your own initializers for specified tags rather than using Syck or Psyck. Accepts a hash with string tags for keys and lambdas for values.
|
95
|
+
|
96
|
+
- `:raise_on_unknown_tag` (default: `false`): Represents the highest possible level of paranoia (not necessarily a bad thing); if the YAML engine encounters any tag other than ones that are automatically trusted by SafeYAML or that you've explicitly whitelisted, it will raise an exception. This may be a good choice if you expect to always be dealing with perfectly safe YAML and want your application to fail loudly upon encountering questionable data.
|
97
|
+
|
98
|
+
All of the above options can be set at the global level via `SafeYAML::OPTIONS`. You can also set each one individually per call to `YAML.load`; an option explicitly passed to `load` will take precedence over an option specified globally.
|
99
|
+
|
100
|
+
Supported Types
|
101
|
+
---------------
|
102
|
+
|
103
|
+
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:
|
104
|
+
|
105
|
+
- Hashes
|
106
|
+
- Arrays
|
107
|
+
- Strings
|
108
|
+
- Numbers
|
109
|
+
- Dates
|
110
|
+
- Times
|
111
|
+
- Booleans
|
112
|
+
- Nils
|
113
|
+
|
114
|
+
Again, deserialization of symbols can be enabled globally by setting `SafeYAML::OPTIONS[:deserialize_symbols] = true`, or in a specific call to `YAML.load([some yaml], :deserialize_symbols => true)`.
|
115
|
+
|
116
|
+
Whitelisting Trusted Types
|
117
|
+
--------------------------
|
118
|
+
|
119
|
+
SafeYAML supports whitelisting certain YAML tags for trusted types. This is handy when your application uses YAML to serialize and deserialize certain types not listed above, which you know to be free of any deserialization-related vulnerabilities.
|
120
|
+
|
121
|
+
The easiest way to whitelist types is by calling `SafeYAML.whitelist!`, which can accept a variable number of safe types, e.g.:
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
SafeYAML.whitelist!(FrobDispenser, GobbleFactory)
|
125
|
+
```
|
126
|
+
|
127
|
+
You can also whitelist YAML *tags* via the `:whitelisted_tags` option:
|
128
|
+
|
129
|
+
```ruby
|
130
|
+
# Using Syck
|
131
|
+
SafeYAML::OPTIONS[:whitelisted_tags] = ["tag:ruby.yaml.org,2002:object:OpenStruct"]
|
132
|
+
|
133
|
+
# Using Psych
|
134
|
+
SafeYAML::OPTIONS[:whitelisted_tags] = ["!ruby/object:OpenStruct"]
|
135
|
+
```
|
136
|
+
|
137
|
+
And in case you were wondering: no, this feature will *not* allow would-be attackers to embed untrusted types within trusted types:
|
138
|
+
|
139
|
+
```ruby
|
140
|
+
yaml = <<-EOYAML
|
141
|
+
--- !ruby/object:OpenStruct
|
142
|
+
table:
|
143
|
+
:backdoor: !ruby/hash:ClassBuilder
|
144
|
+
"foo; end; puts %(I'm in yr system!); def bar": "baz"
|
145
|
+
EOYAML
|
146
|
+
```
|
147
|
+
|
148
|
+
> YAML.safe_load(yaml)
|
149
|
+
=> #<OpenStruct :backdoor={"foo; end; puts %(I'm in yr system!); def bar"=>"baz"}>
|
150
|
+
|
151
|
+
Known Issues
|
152
|
+
------------
|
153
|
+
|
154
|
+
If you add SafeYAML to your project and start seeing any errors about missing keys, or you notice mysterious strings that look like `":foo"` (i.e., start with a colon), it's likely you're seeing errors from symbols being saved in YAML format. If you are able to modify the offending code, you might want to consider changing your YAML content to use plain vanilla strings instead of symbols. If not, you may need to set the `:deserialize_symbols` option to `true`, either in calls to `YAML.load` or--as a last resort--globally, with `SafeYAML::OPTIONS[:deserialize_symbols]`.
|
155
|
+
|
156
|
+
Also be aware 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:
|
157
|
+
|
158
|
+
- [**ActiveRecord**](https://github.com/rails/rails/tree/master/activerecord): uses YAML to control serialization of model objects using the `serialize` class method. If you find that accessing serialized properties on your ActiveRecord models is causing errors, chances are you may need to:
|
159
|
+
1. set the `:deserialize_symbols` option to `true`,
|
160
|
+
2. whitelist some of the types in your serialized data via `SafeYAML.whitelist!` or the `:whitelisted_tags` option, or
|
161
|
+
3. both
|
162
|
+
- [**Guard**](https://github.com/guard/guard): Uses YAML as a serialization format for notifications. The data serialized uses symbolic keys, so setting `SafeYAML::OPTIONS[:deserialize_symbols] = true` is necessary to allow Guard to work.
|
163
|
+
- [**sidekiq**](https://github.com/mperham/sidekiq): Uses a YAML configiuration file with symbolic keys, so setting `SafeYAML::OPTIONS[:deserialize_symbols] = true` should allow it to work.
|
164
|
+
|
165
|
+
The above list will grow over time, as more issues are discovered.
|
166
|
+
|
167
|
+
Caveat
|
168
|
+
------
|
169
|
+
|
170
|
+
My intention is to eventually adopt [semantic versioning](http://semver.org/) with this gem, if it ever gets to version 1.0 (i.e., doesn't become obsolete by then). Since it isn't there yet, that means that API may well change from one version to the next. Please keep that in mind if you are using it in your application.
|
171
|
+
|
172
|
+
To be clear: my *goal* is for SafeYAML to make it as easy as possible to protect existing applications from object deserialization exploits. Any and all feedback is more than welcome!
|
173
|
+
|
174
|
+
Requirements
|
175
|
+
------------
|
176
|
+
|
177
|
+
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).
|
178
|
+
|
179
|
+
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.
|
@@ -0,0 +1,253 @@
|
|
1
|
+
require "yaml"
|
2
|
+
|
3
|
+
# This needs to be defined up front in case any internal classes need to base
|
4
|
+
# their behavior off of this.
|
5
|
+
module SafeYAML
|
6
|
+
YAML_ENGINE = defined?(YAML::ENGINE) ? YAML::ENGINE.yamler : "syck"
|
7
|
+
end
|
8
|
+
|
9
|
+
require "set"
|
10
|
+
require "safe_yaml/deep"
|
11
|
+
require "safe_yaml/parse/hexadecimal"
|
12
|
+
require "safe_yaml/parse/sexagesimal"
|
13
|
+
require "safe_yaml/parse/date"
|
14
|
+
require "safe_yaml/transform/transformation_map"
|
15
|
+
require "safe_yaml/transform/to_boolean"
|
16
|
+
require "safe_yaml/transform/to_date"
|
17
|
+
require "safe_yaml/transform/to_float"
|
18
|
+
require "safe_yaml/transform/to_integer"
|
19
|
+
require "safe_yaml/transform/to_nil"
|
20
|
+
require "safe_yaml/transform/to_symbol"
|
21
|
+
require "safe_yaml/transform"
|
22
|
+
require "safe_yaml/resolver"
|
23
|
+
require "safe_yaml/syck_hack" if defined?(JRUBY_VERSION)
|
24
|
+
|
25
|
+
module SafeYAML
|
26
|
+
MULTI_ARGUMENT_YAML_LOAD = YAML.method(:load).arity != 1
|
27
|
+
|
28
|
+
DEFAULT_OPTIONS = Deep.freeze({
|
29
|
+
:default_mode => nil,
|
30
|
+
:suppress_warnings => false,
|
31
|
+
:deserialize_symbols => false,
|
32
|
+
:whitelisted_tags => [],
|
33
|
+
:custom_initializers => {},
|
34
|
+
:raise_on_unknown_tag => false
|
35
|
+
})
|
36
|
+
|
37
|
+
OPTIONS = Deep.copy(DEFAULT_OPTIONS)
|
38
|
+
|
39
|
+
module_function
|
40
|
+
def restore_defaults!
|
41
|
+
OPTIONS.clear.merge!(Deep.copy(DEFAULT_OPTIONS))
|
42
|
+
end
|
43
|
+
|
44
|
+
def tag_safety_check!(tag, options)
|
45
|
+
return if tag.nil? || tag == "!"
|
46
|
+
if options[:raise_on_unknown_tag] && !options[:whitelisted_tags].include?(tag) && !tag_is_explicitly_trusted?(tag)
|
47
|
+
raise "Unknown YAML tag '#{tag}'"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def whitelist!(*classes)
|
52
|
+
classes.each do |klass|
|
53
|
+
whitelist_class!(klass)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def whitelist_class!(klass)
|
58
|
+
raise "#{klass} not a Class" unless klass.is_a?(::Class)
|
59
|
+
|
60
|
+
klass_name = klass.name
|
61
|
+
raise "#{klass} cannot be anonymous" if klass_name.nil? || klass_name.empty?
|
62
|
+
|
63
|
+
# Whitelist any built-in YAML tags supplied by Syck or Psych.
|
64
|
+
predefined_tag = predefined_tags[klass]
|
65
|
+
if predefined_tag
|
66
|
+
OPTIONS[:whitelisted_tags] << predefined_tag
|
67
|
+
return
|
68
|
+
end
|
69
|
+
|
70
|
+
# Exception is exceptional (har har).
|
71
|
+
tag_class = klass < Exception ? "exception" : "object"
|
72
|
+
|
73
|
+
tag_prefix = case YAML_ENGINE
|
74
|
+
when "psych" then "!ruby/#{tag_class}"
|
75
|
+
when "syck" then "tag:ruby.yaml.org,2002:#{tag_class}"
|
76
|
+
else raise "unknown YAML_ENGINE #{YAML_ENGINE}"
|
77
|
+
end
|
78
|
+
OPTIONS[:whitelisted_tags] << "#{tag_prefix}:#{klass_name}"
|
79
|
+
end
|
80
|
+
|
81
|
+
def predefined_tags
|
82
|
+
if @predefined_tags.nil?
|
83
|
+
@predefined_tags = {}
|
84
|
+
|
85
|
+
if YAML_ENGINE == "syck"
|
86
|
+
YAML.tagged_classes.each do |tag, klass|
|
87
|
+
@predefined_tags[klass] = tag
|
88
|
+
end
|
89
|
+
|
90
|
+
else
|
91
|
+
# Special tags appear to be hard-coded in Psych:
|
92
|
+
# https://github.com/tenderlove/psych/blob/v1.3.4/lib/psych/visitors/to_ruby.rb
|
93
|
+
# Fortunately, there aren't many that SafeYAML doesn't already support.
|
94
|
+
@predefined_tags.merge!({
|
95
|
+
Exception => "!ruby/exception",
|
96
|
+
Range => "!ruby/range",
|
97
|
+
Regexp => "!ruby/regexp",
|
98
|
+
})
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
@predefined_tags
|
103
|
+
end
|
104
|
+
|
105
|
+
if YAML_ENGINE == "psych"
|
106
|
+
def tag_is_explicitly_trusted?(tag)
|
107
|
+
false
|
108
|
+
end
|
109
|
+
|
110
|
+
else
|
111
|
+
TRUSTED_TAGS = Set.new([
|
112
|
+
"tag:yaml.org,2002:binary",
|
113
|
+
"tag:yaml.org,2002:bool#no",
|
114
|
+
"tag:yaml.org,2002:bool#yes",
|
115
|
+
"tag:yaml.org,2002:float",
|
116
|
+
"tag:yaml.org,2002:float#fix",
|
117
|
+
"tag:yaml.org,2002:int",
|
118
|
+
"tag:yaml.org,2002:map",
|
119
|
+
"tag:yaml.org,2002:null",
|
120
|
+
"tag:yaml.org,2002:seq",
|
121
|
+
"tag:yaml.org,2002:str",
|
122
|
+
"tag:yaml.org,2002:timestamp",
|
123
|
+
"tag:yaml.org,2002:timestamp#ymd"
|
124
|
+
]).freeze
|
125
|
+
|
126
|
+
def tag_is_explicitly_trusted?(tag)
|
127
|
+
TRUSTED_TAGS.include?(tag)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
module YAML
|
133
|
+
def self.load_with_options(yaml, *original_arguments)
|
134
|
+
filename, options = filename_and_options_from_arguments(original_arguments)
|
135
|
+
safe_mode = safe_mode_from_options("load", options)
|
136
|
+
arguments = [yaml]
|
137
|
+
|
138
|
+
if safe_mode == :safe
|
139
|
+
arguments << filename if SafeYAML::YAML_ENGINE == "psych"
|
140
|
+
arguments << options_for_safe_load(options)
|
141
|
+
safe_load(*arguments)
|
142
|
+
else
|
143
|
+
arguments << filename if SafeYAML::MULTI_ARGUMENT_YAML_LOAD
|
144
|
+
unsafe_load(*arguments)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def self.load_file_with_options(file, options={})
|
149
|
+
safe_mode = safe_mode_from_options("load_file", options)
|
150
|
+
if safe_mode == :safe
|
151
|
+
safe_load_file(file, options_for_safe_load(options))
|
152
|
+
else
|
153
|
+
unsafe_load_file(file)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
if SafeYAML::YAML_ENGINE == "psych"
|
158
|
+
require "safe_yaml/psych_handler"
|
159
|
+
require "safe_yaml/psych_resolver"
|
160
|
+
require "safe_yaml/safe_to_ruby_visitor"
|
161
|
+
|
162
|
+
def self.safe_load(yaml, filename=nil, options={})
|
163
|
+
# If the user hasn't whitelisted any tags, we can go with this implementation which is
|
164
|
+
# significantly faster.
|
165
|
+
if (options && options[:whitelisted_tags] || SafeYAML::OPTIONS[:whitelisted_tags]).empty?
|
166
|
+
safe_handler = SafeYAML::PsychHandler.new(options)
|
167
|
+
arguments_for_parse = [yaml]
|
168
|
+
arguments_for_parse << filename if SafeYAML::MULTI_ARGUMENT_YAML_LOAD
|
169
|
+
Psych::Parser.new(safe_handler).parse(*arguments_for_parse)
|
170
|
+
return safe_handler.result || false
|
171
|
+
|
172
|
+
else
|
173
|
+
safe_resolver = SafeYAML::PsychResolver.new(options)
|
174
|
+
tree = SafeYAML::MULTI_ARGUMENT_YAML_LOAD ?
|
175
|
+
Psych.parse(yaml, filename) :
|
176
|
+
Psych.parse(yaml)
|
177
|
+
return safe_resolver.resolve_node(tree)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def self.safe_load_file(filename, options={})
|
182
|
+
File.open(filename, 'r:bom|utf-8') { |f| self.safe_load(f, filename, options) }
|
183
|
+
end
|
184
|
+
|
185
|
+
def self.unsafe_load_file(filename)
|
186
|
+
if SafeYAML::MULTI_ARGUMENT_YAML_LOAD
|
187
|
+
# https://github.com/tenderlove/psych/blob/v1.3.2/lib/psych.rb#L296-298
|
188
|
+
File.open(filename, 'r:bom|utf-8') { |f| self.unsafe_load(f, filename) }
|
189
|
+
else
|
190
|
+
# https://github.com/tenderlove/psych/blob/v1.2.2/lib/psych.rb#L231-233
|
191
|
+
self.unsafe_load File.open(filename)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
else
|
196
|
+
require "safe_yaml/syck_resolver"
|
197
|
+
require "safe_yaml/syck_node_monkeypatch"
|
198
|
+
|
199
|
+
def self.safe_load(yaml, options={})
|
200
|
+
resolver = SafeYAML::SyckResolver.new(SafeYAML::OPTIONS.merge(options || {}))
|
201
|
+
tree = YAML.parse(yaml)
|
202
|
+
return resolver.resolve_node(tree)
|
203
|
+
end
|
204
|
+
|
205
|
+
def self.safe_load_file(filename, options={})
|
206
|
+
File.open(filename) { |f| self.safe_load(f, options) }
|
207
|
+
end
|
208
|
+
|
209
|
+
def self.unsafe_load_file(filename)
|
210
|
+
# https://github.com/indeyets/syck/blob/master/ext/ruby/lib/yaml.rb#L133-135
|
211
|
+
File.open(filename) { |f| self.unsafe_load(f) }
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
class << self
|
216
|
+
alias_method :unsafe_load, :load
|
217
|
+
alias_method :load, :load_with_options
|
218
|
+
alias_method :load_file, :load_file_with_options
|
219
|
+
|
220
|
+
private
|
221
|
+
def filename_and_options_from_arguments(arguments)
|
222
|
+
if arguments.count == 1
|
223
|
+
if arguments.first.is_a?(String)
|
224
|
+
return arguments.first, {}
|
225
|
+
else
|
226
|
+
return nil, arguments.first || {}
|
227
|
+
end
|
228
|
+
|
229
|
+
else
|
230
|
+
return arguments.first, arguments.last || {}
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
def safe_mode_from_options(method, options={})
|
235
|
+
if options[:safe].nil?
|
236
|
+
safe_mode = SafeYAML::OPTIONS[:default_mode] || :safe
|
237
|
+
if SafeYAML::OPTIONS[:default_mode].nil? && !SafeYAML::OPTIONS[:suppress_warnings]
|
238
|
+
Kernel.warn "Called '#{method}' without the :safe option -- defaulting to #{safe_mode} mode."
|
239
|
+
SafeYAML::OPTIONS[:suppress_warnings] = true
|
240
|
+
end
|
241
|
+
return safe_mode
|
242
|
+
end
|
243
|
+
|
244
|
+
options[:safe] ? :safe : :unsafe
|
245
|
+
end
|
246
|
+
|
247
|
+
def options_for_safe_load(base_options)
|
248
|
+
options = base_options.dup
|
249
|
+
options.delete(:safe)
|
250
|
+
options
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|