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.

Files changed (101) hide show
  1. data/CHANGELOG +14 -0
  2. data/Gemfile.lock +2 -2
  3. data/ext/build_defaults.yaml +2 -3
  4. data/ext/debian/control +1 -1
  5. data/ext/packaging/README.md +496 -8
  6. data/ext/packaging/spec/tasks/00_utils_spec.rb +7 -7
  7. data/ext/packaging/spec/tasks/build_object_spec.rb +3 -0
  8. data/ext/packaging/tasks/00_utils.rake +2 -2
  9. data/ext/packaging/tasks/10_setupvars.rake +8 -1
  10. data/ext/packaging/tasks/build.rake +2 -0
  11. data/ext/packaging/tasks/deb_repos.rake +48 -15
  12. data/ext/packaging/tasks/jenkins.rake +30 -2
  13. data/ext/packaging/tasks/mock.rake +3 -2
  14. data/ext/packaging/tasks/pe_remote.rake +1 -1
  15. data/ext/packaging/tasks/pe_ship.rake +4 -5
  16. data/ext/packaging/tasks/pe_sign.rake +8 -0
  17. data/ext/packaging/tasks/pe_sles.rake +8 -7
  18. data/ext/packaging/tasks/pre_tasks.rake +0 -0
  19. data/ext/packaging/tasks/retrieve.rake +11 -1
  20. data/ext/packaging/tasks/rpm_repos.rake +71 -49
  21. data/ext/packaging/tasks/ship.rake +14 -2
  22. data/ext/packaging/tasks/sign.rake +9 -3
  23. data/ext/packaging/tasks/tar.rake +5 -0
  24. data/ext/packaging/tasks/vendor_gems.rake +110 -0
  25. data/install.rb +1 -1
  26. data/lib/puppet.rb +11 -0
  27. data/lib/puppet/indirector/report/processor.rb +1 -1
  28. data/lib/puppet/indirector/report/rest.rb +7 -0
  29. data/lib/puppet/indirector/resource/rest.rb +9 -0
  30. data/lib/puppet/indirector/rest.rb +81 -47
  31. data/lib/puppet/indirector/run/rest.rb +6 -0
  32. data/lib/puppet/network/formats.rb +20 -10
  33. data/lib/puppet/network/http/handler.rb +1 -1
  34. data/lib/puppet/node.rb +25 -0
  35. data/lib/puppet/node/facts.rb +23 -4
  36. data/lib/puppet/resource.rb +2 -4
  37. data/lib/puppet/resource/status.rb +28 -0
  38. data/lib/puppet/run.rb +24 -2
  39. data/lib/puppet/status.rb +6 -2
  40. data/lib/puppet/transaction/event.rb +19 -0
  41. data/lib/puppet/transaction/report.rb +39 -0
  42. data/lib/puppet/util/log.rb +19 -0
  43. data/lib/puppet/util/metric.rb +6 -0
  44. data/lib/puppet/util/monkey_patches.rb +0 -16
  45. data/lib/puppet/vendor.rb +55 -0
  46. data/lib/puppet/vendor/load_safe_yaml.rb +1 -0
  47. data/lib/puppet/vendor/require_vendored.rb +4 -0
  48. data/lib/puppet/vendor/safe_yaml/CHANGES.md +104 -0
  49. data/lib/puppet/vendor/safe_yaml/Gemfile +11 -0
  50. data/lib/puppet/vendor/safe_yaml/LICENSE.txt +22 -0
  51. data/lib/puppet/vendor/safe_yaml/README.md +179 -0
  52. data/lib/puppet/vendor/safe_yaml/Rakefile +6 -0
  53. data/lib/puppet/vendor/safe_yaml/lib/safe_yaml.rb +253 -0
  54. data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/deep.rb +34 -0
  55. data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/parse/date.rb +27 -0
  56. data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/parse/hexadecimal.rb +12 -0
  57. data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/parse/sexagesimal.rb +26 -0
  58. data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/psych_handler.rb +92 -0
  59. data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/psych_resolver.rb +52 -0
  60. data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/resolver.rb +94 -0
  61. data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/safe_to_ruby_visitor.rb +17 -0
  62. data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/syck_hack.rb +36 -0
  63. data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/syck_node_monkeypatch.rb +43 -0
  64. data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/syck_resolver.rb +38 -0
  65. data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/transform.rb +41 -0
  66. data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/transform/to_boolean.rb +21 -0
  67. data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/transform/to_date.rb +11 -0
  68. data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/transform/to_float.rb +33 -0
  69. data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/transform/to_integer.rb +25 -0
  70. data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/transform/to_nil.rb +18 -0
  71. data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/transform/to_symbol.rb +13 -0
  72. data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/transform/transformation_map.rb +47 -0
  73. data/lib/puppet/vendor/safe_yaml/lib/safe_yaml/version.rb +3 -0
  74. data/lib/puppet/vendor/safe_yaml/run_specs_all_ruby_versions.sh +21 -0
  75. data/lib/puppet/vendor/safe_yaml/safe_yaml.gemspec +18 -0
  76. data/lib/puppet/vendor/safe_yaml/spec/exploit.1.9.2.yaml +2 -0
  77. data/lib/puppet/vendor/safe_yaml/spec/exploit.1.9.3.yaml +2 -0
  78. data/lib/puppet/vendor/safe_yaml/spec/psych_resolver_spec.rb +10 -0
  79. data/lib/puppet/vendor/safe_yaml/spec/resolver_specs.rb +250 -0
  80. data/lib/puppet/vendor/safe_yaml/spec/safe_yaml_spec.rb +702 -0
  81. data/lib/puppet/vendor/safe_yaml/spec/spec_helper.rb +18 -0
  82. data/lib/puppet/vendor/safe_yaml/spec/support/exploitable_back_door.rb +29 -0
  83. data/lib/puppet/vendor/safe_yaml/spec/syck_resolver_spec.rb +10 -0
  84. data/lib/puppet/vendor/safe_yaml/spec/transform/base64_spec.rb +11 -0
  85. data/lib/puppet/vendor/safe_yaml/spec/transform/to_date_spec.rb +34 -0
  86. data/lib/puppet/vendor/safe_yaml/spec/transform/to_float_spec.rb +42 -0
  87. data/lib/puppet/vendor/safe_yaml/spec/transform/to_integer_spec.rb +59 -0
  88. data/lib/puppet/vendor/safe_yaml/spec/transform/to_symbol_spec.rb +49 -0
  89. data/lib/puppet/version.rb +1 -1
  90. data/spec/lib/puppet_spec/matchers.rb +8 -0
  91. data/spec/unit/file_serving/metadata_spec.rb +20 -28
  92. data/spec/unit/indirector/report/rest_spec.rb +41 -0
  93. data/spec/unit/indirector/rest_spec.rb +314 -339
  94. data/spec/unit/network/formats_spec.rb +36 -27
  95. data/spec/unit/network/http/handler_spec.rb +3 -12
  96. data/spec/unit/node_spec.rb +81 -0
  97. data/spec/unit/resource_spec.rb +5 -35
  98. data/spec/unit/run_spec.rb +22 -8
  99. data/spec/unit/status_spec.rb +6 -0
  100. data/test/network/handler/report.rb +0 -36
  101. metadata +148 -102
@@ -0,0 +1,702 @@
1
+ require File.join(File.dirname(__FILE__), "spec_helper")
2
+
3
+ require "exploitable_back_door"
4
+
5
+ describe YAML do
6
+ # Essentially stolen from:
7
+ # https://github.com/rails/rails/blob/3-2-stable/activesupport/lib/active_support/core_ext/kernel/reporting.rb#L10-25
8
+ def silence_warnings
9
+ $VERBOSE = nil; yield
10
+ ensure
11
+ $VERBOSE = true
12
+ end
13
+
14
+ def safe_load_round_trip(object, options={})
15
+ yaml = object.to_yaml
16
+ if SafeYAML::YAML_ENGINE == "psych"
17
+ YAML.safe_load(yaml, nil, options)
18
+ else
19
+ YAML.safe_load(yaml, options)
20
+ end
21
+ end
22
+
23
+ before :each do
24
+ SafeYAML.restore_defaults!
25
+ end
26
+
27
+ after :each do
28
+ SafeYAML.restore_defaults!
29
+ end
30
+
31
+ describe "unsafe_load" do
32
+ if SafeYAML::YAML_ENGINE == "psych" && RUBY_VERSION >= "1.9.3"
33
+ it "allows exploits through objects defined in YAML w/ !ruby/hash via custom :[]= methods" do
34
+ backdoor = YAML.unsafe_load("--- !ruby/hash:ExploitableBackDoor\nfoo: bar\n")
35
+ backdoor.should be_exploited_through_setter
36
+ end
37
+
38
+ it "allows exploits through objects defined in YAML w/ !ruby/object via the :init_with method" do
39
+ backdoor = YAML.unsafe_load("--- !ruby/object:ExploitableBackDoor\nfoo: bar\n")
40
+ backdoor.should be_exploited_through_init_with
41
+ end
42
+ end
43
+
44
+ it "allows exploits through objects w/ sensitive instance variables defined in YAML w/ !ruby/object" do
45
+ backdoor = YAML.unsafe_load("--- !ruby/object:ExploitableBackDoor\nfoo: bar\n")
46
+ backdoor.should be_exploited_through_ivars
47
+ end
48
+
49
+ context "with special whitelisted tags defined" do
50
+ before :each do
51
+ SafeYAML::whitelist!(OpenStruct)
52
+ end
53
+
54
+ it "effectively ignores the whitelist (since everything is whitelisted)" do
55
+ result = YAML.unsafe_load <<-YAML.unindent
56
+ --- !ruby/object:OpenStruct
57
+ table:
58
+ :backdoor: !ruby/object:ExploitableBackDoor
59
+ foo: bar
60
+ YAML
61
+
62
+ result.should be_a(OpenStruct)
63
+ result.backdoor.should be_exploited_through_ivars
64
+ end
65
+ end
66
+ end
67
+
68
+ describe "safe_load" do
69
+ it "does NOT allow exploits through objects defined in YAML w/ !ruby/hash" do
70
+ object = YAML.safe_load("--- !ruby/hash:ExploitableBackDoor\nfoo: bar\n")
71
+ object.should_not be_a(ExploitableBackDoor)
72
+ end
73
+
74
+ it "does NOT allow exploits through objects defined in YAML w/ !ruby/object" do
75
+ object = YAML.safe_load("--- !ruby/object:ExploitableBackDoor\nfoo: bar\n")
76
+ object.should_not be_a(ExploitableBackDoor)
77
+ end
78
+
79
+ context "for YAML engine #{SafeYAML::YAML_ENGINE}" do
80
+ if SafeYAML::YAML_ENGINE == "psych"
81
+ let(:options) { nil }
82
+ let(:arguments) { ["foo: bar", nil, options] }
83
+
84
+ context "when no tags are whitelisted" do
85
+ it "constructs a SafeYAML::PsychHandler to resolve nodes as they're parsed, for optimal performance" do
86
+ Psych::Parser.should_receive(:new).with an_instance_of(SafeYAML::PsychHandler)
87
+ # This won't work now; we just want to ensure Psych::Parser#parse was in fact called.
88
+ YAML.safe_load(*arguments) rescue nil
89
+ end
90
+ end
91
+
92
+ context "when whitelisted tags are specified" do
93
+ let(:options) {
94
+ { :whitelisted_tags => ["foo"] }
95
+ }
96
+
97
+ it "instead uses Psych to construct a full tree before examining the nodes" do
98
+ Psych.should_receive(:parse)
99
+ # This won't work now; we just want to ensure Psych::Parser#parse was in fact called.
100
+ YAML.safe_load(*arguments) rescue nil
101
+ end
102
+ end
103
+ end
104
+
105
+ if SafeYAML::YAML_ENGINE == "syck"
106
+ it "uses Syck internally to parse YAML" do
107
+ YAML.should_receive(:parse).with("foo: bar")
108
+ # This won't work now; we just want to ensure YAML::parse was in fact called.
109
+ YAML.safe_load("foo: bar") rescue nil
110
+ end
111
+ end
112
+ end
113
+
114
+ it "loads a plain ol' YAML document just fine" do
115
+ result = YAML.safe_load <<-YAML.unindent
116
+ foo:
117
+ number: 1
118
+ string: Hello, there!
119
+ symbol: :blah
120
+ sequence:
121
+ - hi
122
+ - bye
123
+ YAML
124
+
125
+ result.should == {
126
+ "foo" => {
127
+ "number" => 1,
128
+ "string" => "Hello, there!",
129
+ "symbol" => ":blah",
130
+ "sequence" => ["hi", "bye"]
131
+ }
132
+ }
133
+ end
134
+
135
+ it "works for YAML documents with anchors and aliases" do
136
+ result = YAML.safe_load <<-YAML
137
+ - &id001 {}
138
+ - *id001
139
+ - *id001
140
+ YAML
141
+
142
+ result.should == [{}, {}, {}]
143
+ end
144
+
145
+ it "works for YAML documents with binary tagged keys" do
146
+ result = YAML.safe_load <<-YAML
147
+ ? !!binary >
148
+ Zm9v
149
+ : "bar"
150
+ ? !!binary >
151
+ YmFy
152
+ : "baz"
153
+ YAML
154
+
155
+ result.should == {"foo" => "bar", "bar" => "baz"}
156
+ end
157
+
158
+ it "works for YAML documents with binary tagged values" do
159
+ result = YAML.safe_load <<-YAML
160
+ "foo": !!binary >
161
+ YmFy
162
+ "bar": !!binary >
163
+ YmF6
164
+ YAML
165
+
166
+ result.should == {"foo" => "bar", "bar" => "baz"}
167
+ end
168
+
169
+ it "works for YAML documents with binary tagged array values" do
170
+ result = YAML.safe_load <<-YAML
171
+ - !binary |-
172
+ Zm9v
173
+ - !binary |-
174
+ YmFy
175
+ YAML
176
+
177
+ result.should == ["foo", "bar"]
178
+ end
179
+
180
+ it "works for YAML documents with sections" do
181
+ result = YAML.safe_load <<-YAML
182
+ mysql: &mysql
183
+ adapter: mysql
184
+ pool: 30
185
+ login: &login
186
+ username: user
187
+ password: password123
188
+ development: &development
189
+ <<: *mysql
190
+ <<: *login
191
+ host: localhost
192
+ YAML
193
+
194
+ result.should == {
195
+ "mysql" => {
196
+ "adapter" => "mysql",
197
+ "pool" => 30
198
+ },
199
+ "login" => {
200
+ "username" => "user",
201
+ "password" => "password123"
202
+ },
203
+ "development" => {
204
+ "adapter" => "mysql",
205
+ "pool" => 30,
206
+ "username" => "user",
207
+ "password" => "password123",
208
+ "host" => "localhost"
209
+ }
210
+ }
211
+ end
212
+
213
+ it "correctly prefers explicitly defined values over default values from included sections" do
214
+ # Repeating this test 100 times to increase the likelihood of running into an issue caused by
215
+ # non-deterministic hash key enumeration.
216
+ 100.times do
217
+ result = YAML.safe_load <<-YAML
218
+ defaults: &defaults
219
+ foo: foo
220
+ bar: bar
221
+ baz: baz
222
+ custom:
223
+ <<: *defaults
224
+ bar: custom_bar
225
+ baz: custom_baz
226
+ YAML
227
+
228
+ result["custom"].should == {
229
+ "foo" => "foo",
230
+ "bar" => "custom_bar",
231
+ "baz" => "custom_baz"
232
+ }
233
+ end
234
+ end
235
+
236
+ it "works with multi-level inheritance" do
237
+ result = YAML.safe_load <<-YAML
238
+ defaults: &defaults
239
+ foo: foo
240
+ bar: bar
241
+ baz: baz
242
+ custom: &custom
243
+ <<: *defaults
244
+ bar: custom_bar
245
+ baz: custom_baz
246
+ grandcustom: &grandcustom
247
+ <<: *custom
248
+ YAML
249
+
250
+ result.should == {
251
+ "defaults" => { "foo" => "foo", "bar" => "bar", "baz" => "baz" },
252
+ "custom" => { "foo" => "foo", "bar" => "custom_bar", "baz" => "custom_baz" },
253
+ "grandcustom" => { "foo" => "foo", "bar" => "custom_bar", "baz" => "custom_baz" }
254
+ }
255
+ end
256
+
257
+ it "returns false when parsing an empty document" do
258
+ result = YAML.safe_load ""
259
+ result.should == false
260
+ end
261
+
262
+ context "with custom initializers defined" do
263
+ before :each do
264
+ if SafeYAML::YAML_ENGINE == "psych"
265
+ SafeYAML::OPTIONS[:custom_initializers] = {
266
+ "!set" => lambda { Set.new },
267
+ "!hashiemash" => lambda { Hashie::Mash.new }
268
+ }
269
+ else
270
+ SafeYAML::OPTIONS[:custom_initializers] = {
271
+ "tag:yaml.org,2002:set" => lambda { Set.new },
272
+ "tag:yaml.org,2002:hashiemash" => lambda { Hashie::Mash.new }
273
+ }
274
+ end
275
+ end
276
+
277
+ it "will use a custom initializer to instantiate an array-like class upon deserialization" do
278
+ result = YAML.safe_load <<-YAML.unindent
279
+ --- !set
280
+ - 1
281
+ - 2
282
+ - 3
283
+ YAML
284
+
285
+ result.should be_a(Set)
286
+ result.to_a.should =~ [1, 2, 3]
287
+ end
288
+
289
+ it "will use a custom initializer to instantiate a hash-like class upon deserialization" do
290
+ result = YAML.safe_load <<-YAML.unindent
291
+ --- !hashiemash
292
+ foo: bar
293
+ YAML
294
+
295
+ result.should be_a(Hashie::Mash)
296
+ result.to_hash.should == { "foo" => "bar" }
297
+ end
298
+ end
299
+
300
+ context "with special whitelisted tags defined" do
301
+ before :each do
302
+ SafeYAML::whitelist!(OpenStruct)
303
+
304
+ # Necessary for deserializing OpenStructs properly.
305
+ SafeYAML::OPTIONS[:deserialize_symbols] = true
306
+ end
307
+
308
+ it "will allow objects to be deserialized for whitelisted tags" do
309
+ result = YAML.safe_load("--- !ruby/object:OpenStruct\ntable:\n foo: bar\n")
310
+ result.should be_a(OpenStruct)
311
+ result.instance_variable_get(:@table).should == { "foo" => "bar" }
312
+ end
313
+
314
+ it "will not deserialize objects without whitelisted tags" do
315
+ result = YAML.safe_load("--- !ruby/hash:ExploitableBackDoor\nfoo: bar\n")
316
+ result.should_not be_a(ExploitableBackDoor)
317
+ result.should == { "foo" => "bar" }
318
+ end
319
+
320
+ it "will not allow non-whitelisted objects to be embedded within objects with whitelisted tags" do
321
+ result = YAML.safe_load <<-YAML.unindent
322
+ --- !ruby/object:OpenStruct
323
+ table:
324
+ :backdoor: !ruby/object:ExploitableBackDoor
325
+ foo: bar
326
+ YAML
327
+
328
+ result.should be_a(OpenStruct)
329
+ result.backdoor.should_not be_a(ExploitableBackDoor)
330
+ result.backdoor.should == { "foo" => "bar" }
331
+ end
332
+
333
+ context "with the :raise_on_unknown_tag option enabled" do
334
+ before :each do
335
+ SafeYAML::OPTIONS[:raise_on_unknown_tag] = true
336
+ end
337
+
338
+ after :each do
339
+ SafeYAML.restore_defaults!
340
+ end
341
+
342
+ it "raises an exception if a non-nil, non-whitelisted tag is encountered" do
343
+ lambda {
344
+ YAML.safe_load <<-YAML.unindent
345
+ --- !ruby/object:Unknown
346
+ foo: bar
347
+ YAML
348
+ }.should raise_error
349
+ end
350
+
351
+ it "checks all tags, even those within objects with trusted tags" do
352
+ lambda {
353
+ YAML.safe_load <<-YAML.unindent
354
+ --- !ruby/object:OpenStruct
355
+ table:
356
+ :backdoor: !ruby/object:Unknown
357
+ foo: bar
358
+ YAML
359
+ }.should raise_error
360
+ end
361
+
362
+ it "does not raise an exception as long as all tags are whitelisted" do
363
+ result = YAML.safe_load <<-YAML.unindent
364
+ --- !ruby/object:OpenStruct
365
+ table:
366
+ :backdoor:
367
+ string: foo
368
+ integer: 1
369
+ float: 3.14
370
+ symbol: :bar
371
+ date: 2013-02-20
372
+ array: []
373
+ hash: {}
374
+ YAML
375
+
376
+ result.should be_a(OpenStruct)
377
+ result.backdoor.should == {
378
+ "string" => "foo",
379
+ "integer" => 1,
380
+ "float" => 3.14,
381
+ "symbol" => :bar,
382
+ "date" => Date.parse("2013-02-20"),
383
+ "array" => [],
384
+ "hash" => {}
385
+ }
386
+ end
387
+
388
+ it "does not raise an exception on the non-specific '!' tag" do
389
+ result = nil
390
+ expect { result = YAML.safe_load "--- ! 'foo'" }.to_not raise_error
391
+ result.should == "foo"
392
+ end
393
+
394
+ context "with whitelisted custom class" do
395
+ class SomeClass
396
+ attr_accessor :foo
397
+ end
398
+ let(:instance) { SomeClass.new }
399
+
400
+ before do
401
+ SafeYAML::whitelist!(SomeClass)
402
+ instance.foo = 'with trailing whitespace: '
403
+ end
404
+
405
+ it "does not raise an exception on the non-specific '!' tag" do
406
+ result = nil
407
+ expect { result = YAML.safe_load(instance.to_yaml) }.to_not raise_error
408
+ result.foo.should == 'with trailing whitespace: '
409
+ end
410
+ end
411
+ end
412
+ end
413
+
414
+ context "when options are passed direclty to #load which differ from the defaults" do
415
+ let(:default_options) { {} }
416
+
417
+ before :each do
418
+ SafeYAML::OPTIONS.merge!(default_options)
419
+ end
420
+
421
+ context "(for example, when symbol deserialization is enabled by default)" do
422
+ let(:default_options) { { :deserialize_symbols => true } }
423
+
424
+ it "goes with the default option when it is not overridden" do
425
+ silence_warnings do
426
+ YAML.load(":foo: bar").should == { :foo => "bar" }
427
+ end
428
+ end
429
+
430
+ it "allows the default option to be overridden on a per-call basis" do
431
+ silence_warnings do
432
+ YAML.load(":foo: bar", :deserialize_symbols => false).should == { ":foo" => "bar" }
433
+ YAML.load(":foo: bar", :deserialize_symbols => true).should == { :foo => "bar" }
434
+ end
435
+ end
436
+ end
437
+
438
+ context "(or, for example, when certain tags are whitelisted)" do
439
+ let(:default_options) {
440
+ {
441
+ :deserialize_symbols => true,
442
+ :whitelisted_tags => SafeYAML::YAML_ENGINE == "psych" ?
443
+ ["!ruby/object:OpenStruct"] :
444
+ ["tag:ruby.yaml.org,2002:object:OpenStruct"]
445
+ }
446
+ }
447
+
448
+ it "goes with the default option when it is not overridden" do
449
+ result = safe_load_round_trip(OpenStruct.new(:foo => "bar"))
450
+ result.should be_a(OpenStruct)
451
+ result.foo.should == "bar"
452
+ end
453
+
454
+ it "allows the default option to be overridden on a per-call basis" do
455
+ result = safe_load_round_trip(OpenStruct.new(:foo => "bar"), :whitelisted_tags => [])
456
+ result.should == { "table" => { :foo => "bar" } }
457
+
458
+ result = safe_load_round_trip(OpenStruct.new(:foo => "bar"), :deserialize_symbols => false, :whitelisted_tags => [])
459
+ result.should == { "table" => { ":foo" => "bar" } }
460
+ end
461
+ end
462
+ end
463
+ end
464
+
465
+ describe "unsafe_load_file" do
466
+ if SafeYAML::YAML_ENGINE == "psych" && RUBY_VERSION >= "1.9.3"
467
+ it "allows exploits through objects defined in YAML w/ !ruby/hash via custom :[]= methods" do
468
+ backdoor = YAML.unsafe_load_file "spec/exploit.1.9.3.yaml"
469
+ backdoor.should be_exploited_through_setter
470
+ end
471
+ end
472
+
473
+ if SafeYAML::YAML_ENGINE == "psych" && RUBY_VERSION >= "1.9.2"
474
+ it "allows exploits through objects defined in YAML w/ !ruby/object via the :init_with method" do
475
+ backdoor = YAML.unsafe_load_file "spec/exploit.1.9.2.yaml"
476
+ backdoor.should be_exploited_through_init_with
477
+ end
478
+ end
479
+
480
+ it "allows exploits through objects w/ sensitive instance variables defined in YAML w/ !ruby/object" do
481
+ backdoor = YAML.unsafe_load_file "spec/exploit.1.9.2.yaml"
482
+ backdoor.should be_exploited_through_ivars
483
+ end
484
+ end
485
+
486
+ describe "safe_load_file" do
487
+ it "does NOT allow exploits through objects defined in YAML w/ !ruby/hash" do
488
+ object = YAML.safe_load_file "spec/exploit.1.9.3.yaml"
489
+ object.should_not be_a(ExploitableBackDoor)
490
+ end
491
+
492
+ it "does NOT allow exploits through objects defined in YAML w/ !ruby/object" do
493
+ object = YAML.safe_load_file "spec/exploit.1.9.2.yaml"
494
+ object.should_not be_a(ExploitableBackDoor)
495
+ end
496
+ end
497
+
498
+ describe "load" do
499
+ let(:options) { {} }
500
+
501
+ let (:arguments) {
502
+ if SafeYAML::MULTI_ARGUMENT_YAML_LOAD
503
+ ["foo: bar", nil, options]
504
+ else
505
+ ["foo: bar", options]
506
+ end
507
+ }
508
+
509
+ context "as long as a :default_mode has been specified" do
510
+ it "doesn't issue a warning for safe mode, since an explicit mode has been set" do
511
+ SafeYAML::OPTIONS[:default_mode] = :safe
512
+ Kernel.should_not_receive(:warn)
513
+ YAML.load(*arguments)
514
+ end
515
+
516
+ it "doesn't issue a warning for unsafe mode, since an explicit mode has been set" do
517
+ SafeYAML::OPTIONS[:default_mode] = :unsafe
518
+ Kernel.should_not_receive(:warn)
519
+ YAML.load(*arguments)
520
+ end
521
+ end
522
+
523
+ context "when the :safe options is specified" do
524
+ let(:safe_mode) { true }
525
+ let(:options) { { :safe => safe_mode } }
526
+
527
+ it "doesn't issue a warning" do
528
+ Kernel.should_not_receive(:warn)
529
+ YAML.load(*arguments)
530
+ end
531
+
532
+ it "calls #safe_load if the :safe option is set to true" do
533
+ YAML.should_receive(:safe_load)
534
+ YAML.load(*arguments)
535
+ end
536
+
537
+ context "when the :safe option is set to false" do
538
+ let(:safe_mode) { false }
539
+
540
+ it "calls #unsafe_load if the :safe option is set to false" do
541
+ YAML.should_receive(:unsafe_load)
542
+ YAML.load(*arguments)
543
+ end
544
+ end
545
+ end
546
+
547
+ it "issues a warning when the :safe option is omitted" do
548
+ silence_warnings do
549
+ Kernel.should_receive(:warn)
550
+ YAML.load(*arguments)
551
+ end
552
+ end
553
+
554
+ it "only issues a warning once (to avoid spamming an app's output)" do
555
+ silence_warnings do
556
+ Kernel.should_receive(:warn).once
557
+ 2.times { YAML.load(*arguments) }
558
+ end
559
+ end
560
+
561
+ it "defaults to safe mode if the :safe option is omitted" do
562
+ silence_warnings do
563
+ YAML.should_receive(:safe_load)
564
+ YAML.load(*arguments)
565
+ end
566
+ end
567
+
568
+ context "with the default mode set to :unsafe" do
569
+ before :each do
570
+ SafeYAML::OPTIONS[:default_mode] = :unsafe
571
+ end
572
+
573
+ it "defaults to unsafe mode if the :safe option is omitted" do
574
+ silence_warnings do
575
+ YAML.should_receive(:unsafe_load)
576
+ YAML.load(*arguments)
577
+ end
578
+ end
579
+
580
+ it "calls #safe_load if the :safe option is set to true" do
581
+ YAML.should_receive(:safe_load)
582
+ YAML.load(*(arguments + [{ :safe => true }]))
583
+ end
584
+ end
585
+ end
586
+
587
+ describe "load_file" do
588
+ let(:filename) { "spec/exploit.1.9.2.yaml" } # doesn't really matter
589
+
590
+ it "issues a warning if the :safe option is omitted" do
591
+ silence_warnings do
592
+ Kernel.should_receive(:warn)
593
+ YAML.load_file(filename)
594
+ end
595
+ end
596
+
597
+ it "doesn't issue a warning as long as the :safe option is specified" do
598
+ Kernel.should_not_receive(:warn)
599
+ YAML.load_file(filename, :safe => true)
600
+ end
601
+
602
+ it "defaults to safe mode if the :safe option is omitted" do
603
+ silence_warnings do
604
+ YAML.should_receive(:safe_load_file)
605
+ YAML.load_file(filename)
606
+ end
607
+ end
608
+
609
+ it "calls #safe_load_file if the :safe option is set to true" do
610
+ YAML.should_receive(:safe_load_file)
611
+ YAML.load_file(filename, :safe => true)
612
+ end
613
+
614
+ it "calls #unsafe_load_file if the :safe option is set to false" do
615
+ YAML.should_receive(:unsafe_load_file)
616
+ YAML.load_file(filename, :safe => false)
617
+ end
618
+
619
+ context "with arbitrary object deserialization enabled by default" do
620
+ before :each do
621
+ SafeYAML::OPTIONS[:default_mode] = :unsafe
622
+ end
623
+
624
+ it "defaults to unsafe mode if the :safe option is omitted" do
625
+ silence_warnings do
626
+ YAML.should_receive(:unsafe_load_file)
627
+ YAML.load_file(filename)
628
+ end
629
+ end
630
+
631
+ it "calls #safe_load if the :safe option is set to true" do
632
+ YAML.should_receive(:safe_load_file)
633
+ YAML.load_file(filename, :safe => true)
634
+ end
635
+ end
636
+ end
637
+
638
+ describe "whitelist!" do
639
+ context "not a class" do
640
+ it "should raise" do
641
+ expect { SafeYAML::whitelist! :foo }.to raise_error(/not a Class/)
642
+ SafeYAML::OPTIONS[:whitelisted_tags].should be_empty
643
+ end
644
+ end
645
+
646
+ context "anonymous class" do
647
+ it "should raise" do
648
+ expect { SafeYAML::whitelist! Class.new }.to raise_error(/cannot be anonymous/)
649
+ SafeYAML::OPTIONS[:whitelisted_tags].should be_empty
650
+ end
651
+ end
652
+
653
+ context "with a Class as its argument" do
654
+ it "should configure correctly" do
655
+ expect { SafeYAML::whitelist! OpenStruct }.to_not raise_error
656
+ SafeYAML::OPTIONS[:whitelisted_tags].grep(/OpenStruct\Z/).should_not be_empty
657
+ end
658
+
659
+ it "successfully deserializes the specified class" do
660
+ SafeYAML.whitelist!(OpenStruct)
661
+
662
+ # necessary for properly assigning OpenStruct attributes
663
+ SafeYAML::OPTIONS[:deserialize_symbols] = true
664
+
665
+ result = safe_load_round_trip(OpenStruct.new(:foo => "bar"))
666
+ result.should be_a(OpenStruct)
667
+ result.foo.should == "bar"
668
+ end
669
+
670
+ it "works for ranges" do
671
+ SafeYAML.whitelist!(Range)
672
+ safe_load_round_trip(1..10).should == (1..10)
673
+ end
674
+
675
+ it "works for regular expressions" do
676
+ SafeYAML.whitelist!(Regexp)
677
+ safe_load_round_trip(/foo/).should == /foo/
678
+ end
679
+
680
+ it "works for multiple classes" do
681
+ SafeYAML.whitelist!(Range, Regexp)
682
+ safe_load_round_trip([(1..10), /bar/]).should == [(1..10), /bar/]
683
+ end
684
+
685
+ it "works for arbitrary Exception subclasses" do
686
+ class CustomException < Exception
687
+ attr_reader :custom_message
688
+
689
+ def initialize(custom_message)
690
+ @custom_message = custom_message
691
+ end
692
+ end
693
+
694
+ SafeYAML.whitelist!(CustomException)
695
+
696
+ ex = safe_load_round_trip(CustomException.new("blah"))
697
+ ex.should be_a(CustomException)
698
+ ex.custom_message.should == "blah"
699
+ end
700
+ end
701
+ end
702
+ end