safe_yaml-instructure 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ module SafeYAML
2
+ VERSION = "0.8.0"
3
+ end
@@ -0,0 +1,74 @@
1
+ module SafeYAML
2
+ class Whitelist
3
+ attr_reader :allowed
4
+
5
+ def initialize
6
+ reset!
7
+ end
8
+
9
+ def check(tag, value)
10
+ @allowed.each do |ok, checker|
11
+ if ok === tag
12
+ check = check_value(ok, checker, value)
13
+ return check if check
14
+ end
15
+ end
16
+ nil
17
+ end
18
+
19
+ def check_value(tag, checker, value)
20
+ if checker == true
21
+ return :cacheable
22
+ end
23
+
24
+ if @cached[tag][value]
25
+ return :cacheable
26
+ end
27
+
28
+ result = checker.call(value)
29
+ if result == :cacheable
30
+ @cached[tag][value] = true
31
+ return :cacheable
32
+ elsif result
33
+ return :allowed
34
+ else
35
+ return nil
36
+ end
37
+ end
38
+
39
+ def reset!
40
+ @allowed = {}
41
+ @cached = {}
42
+ if SafeYAML::YAML_ENGINE == "psych"
43
+ # psych doesn't tag the default types, except for binary
44
+ add("!binary",
45
+ "tag:yaml.org,2002:binary")
46
+ else
47
+ add("tag:yaml.org,2002:str",
48
+ "tag:yaml.org,2002:int",
49
+ "tag:yaml.org,2002:float",
50
+ "tag:yaml.org,2002:binary",
51
+ "tag:yaml.org,2002:merge",
52
+ "tag:yaml.org,2002:null",
53
+ %r{^tag:yaml.org,2002:bool#},
54
+ %r{^tag:yaml.org,2002:float#},
55
+ %r{^tag:yaml.org,2002:timestamp#},
56
+ "tag:ruby.yaml.org,2002:object:YAML::Syck::BadAlias")
57
+ end
58
+ end
59
+
60
+ def add(*tags, &block)
61
+ tags.each do |tag|
62
+ @cached[tag] = {} if block
63
+ @allowed[tag] = block || true
64
+ end
65
+ end
66
+
67
+ def remove(*tags)
68
+ tags.each do |tag|
69
+ @cached.delete(tag)
70
+ @allowed.delete(tag)
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,21 @@
1
+ #!/bin/bash
2
+
3
+ [[ -s "$HOME/.rvm/scripts/rvm" ]] && . "$HOME/.rvm/scripts/rvm"
4
+
5
+ rvm use 1.8.7@safe_yaml
6
+ rake spec
7
+
8
+ rvm use 1.9.2@safe_yaml
9
+ YAMLER=syck rake spec
10
+
11
+ rvm use 1.9.3@safe_yaml
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
19
+
20
+ rvm use 2.0.0@safe_yaml
21
+ YAMLER=psych rake spec
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.join(File.dirname(__FILE__), "lib", "safe_yaml", "version")
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.name = "safe_yaml-instructure"
6
+ gem.version = SafeYAML::VERSION
7
+ gem.authors = ["Dan Tao", "Brian Palmer"]
8
+ gem.email = "brianp@instructure.com"
9
+ gem.description = %q{Parse YAML safely, without that pesky arbitrary object deserialization vulnerability}
10
+ gem.summary = %q{SameYAML provides an alternative implementation of YAML.load suitable for accepting user input in Ruby applications.}
11
+ gem.homepage = "http://github.com/instructure/safe_yaml/"
12
+ gem.license = "MIT"
13
+ gem.files = `git ls-files`.split($\)
14
+ gem.test_files = gem.files.grep(%r{^spec/})
15
+ gem.require_paths = ["lib"]
16
+
17
+ gem.required_ruby_version = ">= 1.8.7"
18
+
19
+ gem.add_development_dependency "hashie"
20
+ gem.add_development_dependency "heredoc_unindent"
21
+ gem.add_development_dependency "rake"
22
+ gem.add_development_dependency "rspec"
23
+ gem.add_development_dependency "travis-lint"
24
+ end
@@ -0,0 +1,2 @@
1
+ --- !ruby/object:ExploitableBackDoor
2
+ foo: bar
@@ -0,0 +1,2 @@
1
+ --- !ruby/hash:ExploitableBackDoor
2
+ foo: bar
@@ -0,0 +1,3 @@
1
+ ---
2
+ a: 1
3
+ b: 2
@@ -0,0 +1,541 @@
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
+ before :each do
15
+ YAML.disable_symbol_parsing!
16
+ end
17
+
18
+ describe "unsafe_load" do
19
+ if SafeYAML::YAML_ENGINE == "psych" && RUBY_VERSION >= "1.9.3"
20
+ it "allows exploits through objects defined in YAML w/ !ruby/hash via custom :[]= methods" do
21
+ backdoor = YAML.unsafe_load("--- !ruby/hash:ExploitableBackDoor\nfoo: bar\n")
22
+ backdoor.should be_exploited_through_setter
23
+ end
24
+
25
+ it "allows exploits through objects defined in YAML w/ !ruby/object via the :init_with method" do
26
+ backdoor = YAML.unsafe_load("--- !ruby/object:ExploitableBackDoor\nfoo: bar\n")
27
+ backdoor.should be_exploited_through_init_with
28
+ end
29
+ end
30
+
31
+ it "allows exploits through objects w/ sensitive instance variables defined in YAML w/ !ruby/object" do
32
+ backdoor = YAML.unsafe_load("--- !ruby/object:ExploitableBackDoor\nfoo: bar\n")
33
+ backdoor.should be_exploited_through_ivars
34
+ end
35
+
36
+ it "allows exploits through sequenced objects" do
37
+ yaml = <<-YAML.unindent
38
+ ---
39
+ - 1
40
+ - 2
41
+ - !ruby/object:ExploitableBackDoor
42
+ foo: bar
43
+ YAML
44
+ array = YAML.unsafe_load(yaml)
45
+ array[2].should be_exploited_through_ivars
46
+ end
47
+
48
+ it "allows exploits through nested objects" do
49
+ yaml = <<-YAML.unindent
50
+ ---
51
+ a: 1
52
+ b:
53
+ a: 1
54
+ b: !ruby/object:ExploitableBackDoor
55
+ foo: bar
56
+ YAML
57
+ hash = YAML.unsafe_load(yaml)
58
+ hash['b']['b'].should be_exploited_through_ivars
59
+ end
60
+ end
61
+
62
+ describe "safe_load" do
63
+ it "does NOT allow exploits through objects defined in YAML w/ !ruby/hash" do
64
+ expect { YAML.safe_load("--- !ruby/hash:ExploitableBackDoor\nfoo: bar\n") }.to raise_error(SafeYAML::UnsafeTagError)
65
+ end
66
+
67
+ it "does NOT allow exploits through objects defined in YAML w/ !ruby/object" do
68
+ expect { YAML.safe_load("--- !ruby/object:ExploitableBackDoor\nfoo: bar\n") }.to raise_error(SafeYAML::UnsafeTagError)
69
+ end
70
+
71
+ it "does NOT allow exploits in sequenced objects" do
72
+ yaml = <<-YAML.unindent
73
+ ---
74
+ - 1
75
+ - 2
76
+ - !ruby/object:ExploitableBackDoor
77
+ foo: bar
78
+ YAML
79
+ expect { YAML.safe_load(yaml) }.to raise_error(SafeYAML::UnsafeTagError)
80
+ end
81
+
82
+ it "does NOT allow exploits in nested objects" do
83
+ yaml = <<-YAML.unindent
84
+ ---
85
+ a: 1
86
+ b:
87
+ a: 1
88
+ b: !ruby/object:ExploitableBackDoor
89
+ foo: bar
90
+ YAML
91
+ expect { YAML.safe_load(yaml) }.to raise_error(SafeYAML::UnsafeTagError)
92
+ end
93
+
94
+ context "for YAML engine #{SafeYAML::YAML_ENGINE}" do
95
+ if SafeYAML::YAML_ENGINE == "psych"
96
+ let(:arguments) {
97
+ if SafeYAML::MULTI_ARGUMENT_YAML_LOAD
98
+ ["foo: bar", nil]
99
+ else
100
+ ["foo: bar"]
101
+ end
102
+ }
103
+ it "uses Psych internally to parse YAML" do
104
+ Psych::Parser.any_instance.should_receive(:parse).with(*arguments)
105
+ YAML.should_receive(:unsafe_load)
106
+ # This won't work now; we just want to ensure Psych::Parser#parse was in fact called.
107
+ YAML.safe_load(*arguments)
108
+ end
109
+ end
110
+
111
+ if SafeYAML::YAML_ENGINE == "syck"
112
+ it "uses Syck internally to parse YAML" do
113
+ YAML.should_receive(:parse).with("foo: bar")
114
+ # This won't work now; we just want to ensure YAML::parse was in fact called.
115
+ YAML.safe_load("foo: bar") rescue nil
116
+ end
117
+ end
118
+ end
119
+
120
+ it "loads a plain ol' YAML document just fine" do
121
+ YAML.enable_symbol_parsing!
122
+ result = YAML.safe_load <<-YAML.unindent
123
+ foo:
124
+ number: 1
125
+ float: 1.5
126
+ string: Hello, there!
127
+ symbol: :blah
128
+ quoted_symbol_1: ":blah"
129
+ quoted_symbol_2: ':blah'
130
+ y: true
131
+ n: false
132
+ nada:
133
+ date: 2013-02-14
134
+ time: 2013-02-14 09:49:19.419780000 -07:00
135
+ sequence:
136
+ - hi
137
+ - bye
138
+ YAML
139
+
140
+ result.should == {
141
+ "foo" => {
142
+ "number" => 1,
143
+ "float" => 1.5,
144
+ "string" => "Hello, there!",
145
+ "symbol" => :blah,
146
+ "quoted_symbol_1" => ":blah",
147
+ "quoted_symbol_2" => ":blah",
148
+ "y" => true,
149
+ "n" => false,
150
+ "nada" => nil,
151
+ "date" => YAML.unsafe_load("2013-02-14"),
152
+ "time" => YAML.unsafe_load("2013-02-14 09:49:19.419780000 -07:00"),
153
+ "sequence" => ["hi", "bye"]
154
+ }
155
+ }
156
+ end
157
+
158
+ it "works for YAML documents with anchors and aliases" do
159
+ result = YAML.safe_load <<-YAML
160
+ - &id001 {}
161
+ - *id001
162
+ - *id001
163
+ YAML
164
+
165
+ result.should == [{}, {}, {}]
166
+ end
167
+
168
+ if SafeYAML::YAML_ENGINE == "psych"
169
+ # syck doesn't support !binary
170
+ it "works for YAML documents with binary tagged keys" do
171
+ result = YAML.safe_load <<-YAML
172
+ ? !!binary >
173
+ Zm9v
174
+ : "bar"
175
+ ? !!binary >
176
+ YmFy
177
+ : "baz"
178
+ YAML
179
+
180
+ result.should == {"foo" => "bar", "bar" => "baz"}
181
+ end
182
+
183
+ it "works for YAML documents with binary tagged values" do
184
+ result = YAML.safe_load <<-YAML
185
+ "foo": !!binary >
186
+ YmFy
187
+ "bar": !!binary >
188
+ YmF6
189
+ YAML
190
+
191
+ result.should == {"foo" => "bar", "bar" => "baz"}
192
+ end
193
+
194
+ it "works for YAML documents with binary tagged array values" do
195
+ result = YAML.safe_load <<-YAML
196
+ - !binary |-
197
+ Zm9v
198
+ - !binary |-
199
+ YmFy
200
+ YAML
201
+
202
+ result.should == ["foo", "bar"]
203
+ end
204
+ end
205
+
206
+ it "works for YAML documents with sections" do
207
+ result = YAML.safe_load <<-YAML
208
+ mysql: &mysql
209
+ adapter: mysql
210
+ pool: 30
211
+ login: &login
212
+ username: user
213
+ password: password123
214
+ development: &development
215
+ <<: *mysql
216
+ <<: *login
217
+ host: localhost
218
+ YAML
219
+
220
+ result.should == {
221
+ "mysql" => {
222
+ "adapter" => "mysql",
223
+ "pool" => 30
224
+ },
225
+ "login" => {
226
+ "username" => "user",
227
+ "password" => "password123"
228
+ },
229
+ "development" => {
230
+ "adapter" => "mysql",
231
+ "pool" => 30,
232
+ "username" => "user",
233
+ "password" => "password123",
234
+ "host" => "localhost"
235
+ }
236
+ }
237
+ end
238
+
239
+ it "correctly prefers explicitly defined values over default values from included sections" do
240
+ # Repeating this test 100 times to increase the likelihood of running into an issue caused by
241
+ # non-deterministic hash key enumeration.
242
+ 100.times do
243
+ result = YAML.safe_load <<-YAML
244
+ defaults: &defaults
245
+ foo: foo
246
+ bar: bar
247
+ baz: baz
248
+ custom:
249
+ <<: *defaults
250
+ bar: custom_bar
251
+ baz: custom_baz
252
+ YAML
253
+
254
+ result["custom"].should == {
255
+ "foo" => "foo",
256
+ "bar" => "custom_bar",
257
+ "baz" => "custom_baz"
258
+ }
259
+ end
260
+ end
261
+
262
+ it "works with multi-level inheritance" do
263
+ result = YAML.safe_load <<-YAML
264
+ defaults: &defaults
265
+ foo: foo
266
+ bar: bar
267
+ baz: baz
268
+ custom: &custom
269
+ <<: *defaults
270
+ bar: custom_bar
271
+ baz: custom_baz
272
+ grandcustom: &grandcustom
273
+ <<: *custom
274
+ YAML
275
+
276
+ result.should == {
277
+ "defaults" => { "foo" => "foo", "bar" => "bar", "baz" => "baz" },
278
+ "custom" => { "foo" => "foo", "bar" => "custom_bar", "baz" => "custom_baz" },
279
+ "grandcustom" => { "foo" => "foo", "bar" => "custom_bar", "baz" => "custom_baz" }
280
+ }
281
+ end
282
+
283
+ context "with custom whitelist defined" do
284
+ class MyClass
285
+ attr_reader :a, :b
286
+ def initialize(a, b)
287
+ @a = a
288
+ @b = b
289
+ end
290
+
291
+ def self.new_from_hash(hash)
292
+ self.new(*hash.values_at('a', 'b'))
293
+ end
294
+ end
295
+
296
+ before :each do
297
+ if SafeYAML::YAML_ENGINE == "psych"
298
+ YAML.whitelist.add('!ruby/object:Set',
299
+ '!map:Hashie::Mash',
300
+ '!ruby/object:MyClass')
301
+ YAML.whitelist.add('!ruby/class') { |val| val == 'A' }
302
+ else
303
+ YAML.whitelist.add('tag:ruby.yaml.org,2002:object:Set',
304
+ 'tag:yaml.org,2002:map:Hashie::Mash',
305
+ 'tag:ruby.yaml.org,2002:object:MyClass')
306
+ YAML.whitelist.add('tag:ruby.yaml.org,2002:class') { |val| val == 'A' }
307
+ end
308
+ end
309
+
310
+ after :each do
311
+ YAML.whitelist.reset!
312
+ end
313
+
314
+ it "will allow array-structure classes via the whitelist" do
315
+ result = YAML.safe_load <<-YAML.unindent
316
+ --- !ruby/object:Set
317
+ hash:
318
+ 1: true
319
+ 2: true
320
+ 3: true
321
+ YAML
322
+
323
+ result.should be_a(Set)
324
+ result.to_a.should =~ [1, 2, 3]
325
+ end
326
+
327
+ it "will allow hash-structure classes via the whitelist" do
328
+ pending("1.9.2 psych doesn't load the map class") if SafeYAML::YAML_ENGINE == "psych" && RUBY_VERSION == "1.9.2"
329
+ result = YAML.safe_load <<-YAML.unindent
330
+ --- !map:Hashie::Mash
331
+ foo: bar
332
+ YAML
333
+
334
+ result.should be_a(Hashie::Mash)
335
+ result.to_hash.should == { "foo" => "bar" }
336
+
337
+ result = YAML.safe_load <<-YAML.unindent
338
+ --- !ruby/object:MyClass
339
+ a: 1
340
+ b: !ruby/object:MyClass
341
+ a: &70346695583400 !ruby/object:MyClass
342
+ a: 2
343
+ b: *70346695583400
344
+ b: *70346695583400
345
+ YAML
346
+
347
+ result.should be_a(MyClass)
348
+ result.a.should == 1
349
+ result.b.should be_a(MyClass)
350
+ result.b.a.should be_a(MyClass)
351
+ result.b.a.a.should == 2
352
+ result.b.a.b.object_id.should == result.b.a.object_id
353
+ result.b.a.object_id.should == result.b.b.object_id
354
+ end
355
+
356
+ class A; end
357
+ class B; end
358
+
359
+ it "allows a block in the whitelist to determine safety" do
360
+ res = YAML.safe_load <<-YAML.unindent
361
+ ---
362
+ a: !ruby/class A
363
+ YAML
364
+ if SafeYAML::YAML_ENGINE == "psych" && RUBY_VERSION >= "1.9.3"
365
+ res['a'].should == A
366
+ end
367
+
368
+ yaml = <<-YAML.unindent
369
+ ---
370
+ a: !ruby/class A
371
+ b: !ruby/class B
372
+ YAML
373
+
374
+ expect { YAML.safe_load yaml }.to raise_error(SafeYAML::UnsafeTagError)
375
+ end
376
+ end
377
+ end
378
+
379
+ describe "unsafe_load_file" do
380
+ if SafeYAML::YAML_ENGINE == "psych" && RUBY_VERSION >= "1.9.3"
381
+ it "allows exploits through objects defined in YAML w/ !ruby/hash via custom :[]= methods" do
382
+ backdoor = YAML.unsafe_load_file "spec/exploit.1.9.3.yaml"
383
+ backdoor.should be_exploited_through_setter
384
+ end
385
+ end
386
+
387
+ if SafeYAML::YAML_ENGINE == "psych" && RUBY_VERSION >= "1.9.2"
388
+ it "allows exploits through objects defined in YAML w/ !ruby/object via the :init_with method" do
389
+ backdoor = YAML.unsafe_load_file "spec/exploit.1.9.2.yaml"
390
+ backdoor.should be_exploited_through_init_with
391
+ end
392
+ end
393
+
394
+ it "allows exploits through objects w/ sensitive instance variables defined in YAML w/ !ruby/object" do
395
+ backdoor = YAML.unsafe_load_file "spec/exploit.1.9.2.yaml"
396
+ backdoor.should be_exploited_through_ivars
397
+ end
398
+ end
399
+
400
+ describe "safe_load_file" do
401
+ it "loads successfully" do
402
+ result = YAML.safe_load_file "spec/ok.yaml"
403
+ result.should == { 'a' => 1, 'b' => 2 }
404
+ end
405
+
406
+ it "does NOT allow exploits through objects defined in YAML w/ !ruby/hash" do
407
+ expect { YAML.safe_load_file "spec/exploit.1.9.3.yaml" }.to raise_error(SafeYAML::UnsafeTagError)
408
+ end
409
+
410
+ it "does NOT allow exploits through objects defined in YAML w/ !ruby/object" do
411
+ expect { YAML.safe_load_file "spec/exploit.1.9.2.yaml" }.to raise_error(SafeYAML::UnsafeTagError)
412
+ end
413
+ end
414
+
415
+ describe "load" do
416
+ let(:arguments) {
417
+ if SafeYAML::MULTI_ARGUMENT_YAML_LOAD
418
+ ["foo: bar", nil]
419
+ else
420
+ ["foo: bar"]
421
+ end
422
+ }
423
+
424
+ context "with :suppress_warnings set to true" do
425
+ before :each do SafeYAML::OPTIONS[:suppress_warnings] = true; end
426
+ after :each do SafeYAML::OPTIONS[:suppress_warnings] = false; end
427
+
428
+ it "doesn't issue a warning if :suppress_warnings option is set to true" do
429
+ SafeYAML::OPTIONS[:suppress_warnings] = true
430
+ Kernel.should_not_receive(:warn)
431
+ YAML.load(*arguments)
432
+ end
433
+ end
434
+
435
+ it "issues a warning if the :safe option is omitted" do
436
+ silence_warnings do
437
+ Kernel.should_receive(:warn)
438
+ YAML.load(*arguments)
439
+ end
440
+ end
441
+
442
+ it "doesn't issue a warning as long as the :safe option is specified" do
443
+ Kernel.should_not_receive(:warn)
444
+ YAML.load(*(arguments + [{:safe => true}]))
445
+ end
446
+
447
+ it "defaults to safe mode if the :safe option is omitted" do
448
+ silence_warnings do
449
+ YAML.should_receive(:safe_load).with(*arguments)
450
+ YAML.load(*arguments)
451
+ end
452
+ end
453
+
454
+ it "calls #safe_load if the :safe option is set to true" do
455
+ YAML.should_receive(:safe_load).with(*arguments)
456
+ YAML.load(*(arguments + [{:safe => true}]))
457
+ end
458
+
459
+ it "calls #unsafe_load if the :safe option is set to false" do
460
+ YAML.should_receive(:unsafe_load).with(*arguments)
461
+ YAML.load(*(arguments + [{:safe => false}]))
462
+ end
463
+
464
+ context "with arbitrary object deserialization enabled by default" do
465
+ before :each do
466
+ YAML.enable_arbitrary_object_deserialization!
467
+ end
468
+
469
+ after :each do
470
+ YAML.disable_arbitrary_object_deserialization!
471
+ end
472
+
473
+ it "defaults to unsafe mode if the :safe option is omitted" do
474
+ silence_warnings do
475
+ YAML.should_receive(:unsafe_load).with(*arguments)
476
+ YAML.load(*arguments)
477
+ end
478
+ end
479
+
480
+ it "calls #safe_load if the :safe option is set to true" do
481
+ YAML.should_receive(:safe_load).with(*arguments)
482
+ YAML.load(*(arguments + [{:safe => true}]))
483
+ end
484
+ end
485
+ end
486
+
487
+ describe "load_file" do
488
+ let(:filename) { "spec/ok.yaml" } # doesn't really matter
489
+
490
+ it "issues a warning if the :safe option is omitted" do
491
+ silence_warnings do
492
+ Kernel.should_receive(:warn)
493
+ YAML.load_file(filename)
494
+ end
495
+ end
496
+
497
+ it "doesn't issue a warning as long as the :safe option is specified" do
498
+ Kernel.should_not_receive(:warn)
499
+ YAML.load_file(filename, :safe => true)
500
+ end
501
+
502
+ it "defaults to safe mode if the :safe option is omitted" do
503
+ silence_warnings do
504
+ YAML.should_receive(:safe_load_file).with(filename)
505
+ YAML.load_file(filename)
506
+ end
507
+ end
508
+
509
+ it "calls #safe_load_file if the :safe option is set to true" do
510
+ YAML.should_receive(:safe_load_file).with(filename)
511
+ YAML.load_file(filename, :safe => true)
512
+ end
513
+
514
+ it "calls #unsafe_load_file if the :safe option is set to false" do
515
+ YAML.should_receive(:unsafe_load_file).with(filename)
516
+ YAML.load_file(filename, :safe => false)
517
+ end
518
+
519
+ context "with arbitrary object deserialization enabled by default" do
520
+ before :each do
521
+ YAML.enable_arbitrary_object_deserialization!
522
+ end
523
+
524
+ after :each do
525
+ YAML.disable_arbitrary_object_deserialization!
526
+ end
527
+
528
+ it "defaults to unsafe mode if the :safe option is omitted" do
529
+ silence_warnings do
530
+ YAML.should_receive(:unsafe_load_file).with(filename)
531
+ YAML.load_file(filename)
532
+ end
533
+ end
534
+
535
+ it "calls #safe_load if the :safe option is set to true" do
536
+ YAML.should_receive(:safe_load_file).with(filename)
537
+ YAML.load_file(filename, :safe => true)
538
+ end
539
+ end
540
+ end
541
+ end