~ubuntu-branches/ubuntu/precise/puppet/precise-security

« back to all changes in this revision

Viewing changes to .pc/2.7.11-Patch-CVE-2013-3567.patch/spec/unit/network/http/handler_spec.rb

  • Committer: Package Import Robot
  • Author(s): Marc Deslauriers
  • Date: 2013-06-14 09:06:22 UTC
  • Revision ID: package-import@ubuntu.com-20130614090622-udg81hzxap7nxrow
Tags: 2.7.11-1ubuntu2.3
* SECURITY UPDATE: Remote code execution on master from unauthenticated
  clients
  - debian/patches/2.7.21-Patch-for-CVE-2013-3567.patch: upstream patch
    to use safe_yama.
  - CVE-2013-3567

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env rspec
 
2
require 'spec_helper'
 
3
require 'puppet/network/http/handler'
 
4
require 'puppet/network/rest_authorization'
 
5
 
 
6
class HttpHandled
 
7
  include Puppet::Network::HTTP::Handler
 
8
end
 
9
 
 
10
describe Puppet::Network::HTTP::Handler do
 
11
  before do
 
12
    @handler = HttpHandled.new
 
13
  end
 
14
 
 
15
  it "should include the v1 REST API" do
 
16
    Puppet::Network::HTTP::Handler.ancestors.should be_include(Puppet::Network::HTTP::API::V1)
 
17
  end
 
18
 
 
19
  it "should include the Rest Authorization system" do
 
20
    Puppet::Network::HTTP::Handler.ancestors.should be_include(Puppet::Network::RestAuthorization)
 
21
  end
 
22
 
 
23
  it "should have a method for initializing" do
 
24
    @handler.should respond_to(:initialize_for_puppet)
 
25
  end
 
26
 
 
27
  describe "when initializing" do
 
28
    it "should fail when no server type has been provided" do
 
29
      lambda { @handler.initialize_for_puppet }.should raise_error(ArgumentError)
 
30
    end
 
31
 
 
32
    it "should set server type" do
 
33
      @handler.initialize_for_puppet("foo")
 
34
      @handler.server.should == "foo"
 
35
    end
 
36
  end
 
37
 
 
38
  it "should be able to process requests" do
 
39
    @handler.should respond_to(:process)
 
40
  end
 
41
 
 
42
  describe "when processing a request" do
 
43
    before do
 
44
      @request     = stub('http request')
 
45
      @request.stubs(:[]).returns "foo"
 
46
      @response    = stub('http response')
 
47
      @model_class = stub('indirected model class')
 
48
      @indirection = stub('indirection')
 
49
      @model_class.stubs(:indirection).returns(@indirection)
 
50
 
 
51
      @result = stub 'result', :render => "mytext"
 
52
 
 
53
      @handler.stubs(:check_authorization)
 
54
 
 
55
      stub_server_interface
 
56
    end
 
57
 
 
58
    # Stub out the interface we require our including classes to
 
59
    # implement.
 
60
    def stub_server_interface
 
61
      @handler.stubs(:accept_header      ).returns "format_one,format_two"
 
62
      @handler.stubs(:content_type_header).returns "text/yaml"
 
63
      @handler.stubs(:set_content_type   ).returns "my_result"
 
64
      @handler.stubs(:set_response       ).returns "my_result"
 
65
      @handler.stubs(:path               ).returns "/my_handler/my_result"
 
66
      @handler.stubs(:http_method        ).returns("GET")
 
67
      @handler.stubs(:params             ).returns({})
 
68
      @handler.stubs(:content_type       ).returns("text/plain")
 
69
    end
 
70
 
 
71
    it "should create an indirection request from the path, parameters, and http method" do
 
72
      @handler.expects(:path).with(@request).returns "mypath"
 
73
      @handler.expects(:http_method).with(@request).returns "mymethod"
 
74
      @handler.expects(:params).with(@request).returns "myparams"
 
75
 
 
76
      @handler.expects(:uri2indirection).with("mymethod", "mypath", "myparams").returns stub("request", :method => :find)
 
77
 
 
78
      @handler.stubs(:do_find)
 
79
 
 
80
      @handler.process(@request, @response)
 
81
    end
 
82
 
 
83
    it "should call the 'do' method and delegate authorization to the RestAuthorization layer" do
 
84
      @handler.expects(:uri2indirection).returns(["facts", :mymethod, "key", {:node => "name"}])
 
85
 
 
86
      @handler.expects(:do_mymethod).with("facts", "key", {:node => "name"}, @request, @response)
 
87
 
 
88
      @handler.expects(:check_authorization).with("facts", :mymethod, "key", {:node => "name"})
 
89
 
 
90
      @handler.process(@request, @response)
 
91
    end
 
92
 
 
93
    it "should return 403 if the request is not authorized" do
 
94
      @handler.expects(:uri2indirection).returns(["facts", :mymethod, "key", {:node => "name"}])
 
95
 
 
96
      @handler.expects(:do_mymethod).never
 
97
 
 
98
      @handler.expects(:check_authorization).with("facts", :mymethod, "key", {:node => "name"}).raises(Puppet::Network::AuthorizationError.new("forbidden"))
 
99
 
 
100
      @handler.expects(:set_response).with { |response, body, status| status == 403 }
 
101
 
 
102
      @handler.process(@request, @response)
 
103
    end
 
104
 
 
105
    it "should serialize a controller exception when an exception is thrown while finding the model instance" do
 
106
      @handler.expects(:uri2indirection).returns(["facts", :find, "key", {:node => "name"}])
 
107
 
 
108
      @handler.expects(:do_find).raises(ArgumentError, "The exception")
 
109
      @handler.expects(:set_response).with { |response, body, status| body == "The exception" and status == 400 }
 
110
      @handler.process(@request, @response)
 
111
    end
 
112
 
 
113
    it "should set the format to text/plain when serializing an exception" do
 
114
      @handler.expects(:set_content_type).with(@response, "text/plain")
 
115
      @handler.do_exception(@response, "A test", 404)
 
116
    end
 
117
 
 
118
    it "should raise an error if the request is formatted in an unknown format" do
 
119
      @handler.stubs(:content_type_header).returns "unknown format"
 
120
      lambda { @handler.request_format(@request) }.should raise_error
 
121
    end
 
122
 
 
123
    it "should still find the correct format if content type contains charset information" do
 
124
      @handler.stubs(:content_type_header).returns "text/plain; charset=UTF-8"
 
125
      @handler.request_format(@request).should == "s"
 
126
    end
 
127
 
 
128
    it "should deserialize YAML parameters" do
 
129
      params = {'my_param' => [1,2,3].to_yaml}
 
130
 
 
131
      decoded_params = @handler.send(:decode_params, params)
 
132
 
 
133
      decoded_params.should == {:my_param => [1,2,3]}
 
134
    end
 
135
 
 
136
    it "should accept YAML parameters with !ruby/hash tags on Ruby 1.8", :if => RUBY_VERSION =~ /^1\.8/ do
 
137
      params = {'my_param' => "--- !ruby/hash:Array {}"}
 
138
 
 
139
      decoded_params = @handler.send(:decode_params, params)
 
140
 
 
141
      decoded_params[:my_param].should be_an(Array)
 
142
    end
 
143
 
 
144
    # These are only dangerous with Psych, which is Ruby 1.9-only. Since
 
145
    # there's no real way to change the yamler in Puppet, assume that 1.9 means
 
146
    # Psych, especially in tests.
 
147
    it "should fail if YAML parameters have !ruby/hash tags on Ruby 1.9", :unless => RUBY_VERSION =~ /^1\.8/ do
 
148
      params = {'my_param' => "--- !ruby/hash:Array {}"}
 
149
 
 
150
      expect { @handler.send(:decode_params, params) }.to raise_error(ArgumentError, /Illegal YAML mapping found/)
 
151
    end
 
152
 
 
153
    describe "when finding a model instance" do
 
154
      before do
 
155
        @indirection.stubs(:find).returns @result
 
156
        Puppet::Indirector::Indirection.expects(:instance).with(:my_handler).returns( stub "indirection", :model => @model_class )
 
157
 
 
158
        @format = stub 'format', :suitable? => true, :mime => "text/format", :name => "format"
 
159
        Puppet::Network::FormatHandler.stubs(:format).returns @format
 
160
 
 
161
        @oneformat = stub 'one', :suitable? => true, :mime => "text/one", :name => "one"
 
162
        Puppet::Network::FormatHandler.stubs(:format).with("one").returns @oneformat
 
163
      end
 
164
 
 
165
      it "should use the indirection request to find the model class" do
 
166
        @handler.do_find("my_handler", "my_result", {}, @request, @response)
 
167
      end
 
168
 
 
169
      it "should use the escaped request key" do
 
170
        @indirection.expects(:find).with do |key, args|
 
171
          key == "my_result"
 
172
        end.returns @result
 
173
        @handler.do_find("my_handler", "my_result", {}, @request, @response)
 
174
      end
 
175
 
 
176
      it "should use a common method for determining the request parameters" do
 
177
        @indirection.expects(:find).with do |key, args|
 
178
          args[:foo] == :baz and args[:bar] == :xyzzy
 
179
        end.returns @result
 
180
        @handler.do_find("my_handler", "my_result", {:foo => :baz, :bar => :xyzzy}, @request, @response)
 
181
      end
 
182
 
 
183
      it "should set the content type to the first format specified in the accept header" do
 
184
        @handler.expects(:accept_header).with(@request).returns "one,two"
 
185
        @handler.expects(:set_content_type).with(@response, @oneformat)
 
186
        @handler.do_find("my_handler", "my_result", {}, @request, @response)
 
187
      end
 
188
 
 
189
      it "should fail if no accept header is provided" do
 
190
        @handler.expects(:accept_header).with(@request).returns nil
 
191
        lambda { @handler.do_find("my_handler", "my_result", {}, @request, @response) }.should raise_error(ArgumentError)
 
192
      end
 
193
 
 
194
      it "should fail if the accept header does not contain a valid format" do
 
195
        @handler.expects(:accept_header).with(@request).returns ""
 
196
        lambda { @handler.do_find("my_handler", "my_result", {}, @request, @response) }.should raise_error(RuntimeError)
 
197
      end
 
198
 
 
199
      it "should not use an unsuitable format" do
 
200
        @handler.expects(:accept_header).with(@request).returns "foo,bar"
 
201
        foo = mock 'foo', :suitable? => false
 
202
        bar = mock 'bar', :suitable? => true
 
203
        Puppet::Network::FormatHandler.expects(:format).with("foo").returns foo
 
204
        Puppet::Network::FormatHandler.expects(:format).with("bar").returns bar
 
205
 
 
206
        @handler.expects(:set_content_type).with(@response, bar) # the suitable one
 
207
 
 
208
        @handler.do_find("my_handler", "my_result", {}, @request, @response)
 
209
      end
 
210
 
 
211
      it "should render the result using the first format specified in the accept header" do
 
212
 
 
213
        @handler.expects(:accept_header).with(@request).returns "one,two"
 
214
        @result.expects(:render).with(@oneformat)
 
215
 
 
216
        @handler.do_find("my_handler", "my_result", {}, @request, @response)
 
217
      end
 
218
 
 
219
      it "should pass the result through without rendering it if the result is a string" do
 
220
        @indirection.stubs(:find).returns "foo"
 
221
        @handler.expects(:set_response).with(@response, "foo")
 
222
        @handler.do_find("my_handler", "my_result", {}, @request, @response)
 
223
      end
 
224
 
 
225
      it "should use the default status when a model find call succeeds" do
 
226
        @handler.expects(:set_response).with { |response, body, status| status.nil? }
 
227
        @handler.do_find("my_handler", "my_result", {}, @request, @response)
 
228
      end
 
229
 
 
230
      it "should return a serialized object when a model find call succeeds" do
 
231
        @model_instance = stub('model instance')
 
232
        @model_instance.expects(:render).returns "my_rendered_object"
 
233
 
 
234
        @handler.expects(:set_response).with { |response, body, status| body == "my_rendered_object" }
 
235
        @indirection.stubs(:find).returns(@model_instance)
 
236
        @handler.do_find("my_handler", "my_result", {}, @request, @response)
 
237
      end
 
238
 
 
239
      it "should return a 404 when no model instance can be found" do
 
240
        @model_class.stubs(:name).returns "my name"
 
241
        @handler.expects(:set_response).with { |response, body, status| status == 404 }
 
242
        @indirection.stubs(:find).returns(nil)
 
243
        @handler.do_find("my_handler", "my_result", {}, @request, @response)
 
244
      end
 
245
 
 
246
      it "should write a log message when no model instance can be found" do
 
247
        @model_class.stubs(:name).returns "my name"
 
248
        @indirection.stubs(:find).returns(nil)
 
249
 
 
250
        Puppet.expects(:info).with("Could not find my_handler for 'my_result'")
 
251
 
 
252
        @handler.do_find("my_handler", "my_result", {}, @request, @response)
 
253
      end
 
254
 
 
255
 
 
256
      it "should serialize the result in with the appropriate format" do
 
257
        @model_instance = stub('model instance')
 
258
 
 
259
        @handler.expects(:format_to_use).returns(@oneformat)
 
260
        @model_instance.expects(:render).with(@oneformat).returns "my_rendered_object"
 
261
        @indirection.stubs(:find).returns(@model_instance)
 
262
        @handler.do_find("my_handler", "my_result", {}, @request, @response)
 
263
      end
 
264
    end
 
265
 
 
266
    describe "when performing head operation" do
 
267
      before do
 
268
        @handler.stubs(:model).with("my_handler").returns(stub 'model', :indirection => @model_class)
 
269
        @handler.stubs(:http_method).with(@request).returns("HEAD")
 
270
        @handler.stubs(:path).with(@request).returns("/production/my_handler/my_result")
 
271
        @handler.stubs(:params).with(@request).returns({})
 
272
 
 
273
        @model_class.stubs(:head).returns true
 
274
      end
 
275
 
 
276
      it "should use the escaped request key" do
 
277
        @model_class.expects(:head).with do |key, args|
 
278
          key == "my_result"
 
279
        end.returns true
 
280
        @handler.process(@request, @response)
 
281
      end
 
282
 
 
283
      it "should not generate a response when a model head call succeeds" do
 
284
        @handler.expects(:set_response).never
 
285
        @handler.process(@request, @response)
 
286
      end
 
287
 
 
288
      it "should return a 404 when the model head call returns false" do
 
289
        @handler.expects(:set_response).with { |response, body, status| status == 404 }
 
290
        @model_class.stubs(:head).returns(false)
 
291
        @handler.process(@request, @response)
 
292
      end
 
293
    end
 
294
 
 
295
    describe "when searching for model instances" do
 
296
      before do
 
297
        Puppet::Indirector::Indirection.expects(:instance).with(:my_handler).returns( stub "indirection", :model => @model_class )
 
298
 
 
299
        @result1 = mock 'result1'
 
300
        @result2 = mock 'results'
 
301
 
 
302
        @result = [@result1, @result2]
 
303
        @model_class.stubs(:render_multiple).returns "my rendered instances"
 
304
        @indirection.stubs(:search).returns(@result)
 
305
 
 
306
        @format = stub 'format', :suitable? => true, :mime => "text/format", :name => "format"
 
307
        Puppet::Network::FormatHandler.stubs(:format).returns @format
 
308
 
 
309
        @oneformat = stub 'one', :suitable? => true, :mime => "text/one", :name => "one"
 
310
        Puppet::Network::FormatHandler.stubs(:format).with("one").returns @oneformat
 
311
      end
 
312
 
 
313
      it "should use the indirection request to find the model" do
 
314
        @handler.do_search("my_handler", "my_result", {}, @request, @response)
 
315
      end
 
316
 
 
317
      it "should use a common method for determining the request parameters" do
 
318
        @indirection.expects(:search).with do |key, args|
 
319
          args[:foo] == :baz and args[:bar] == :xyzzy
 
320
        end.returns @result
 
321
        @handler.do_search("my_handler", "my_result", {:foo => :baz, :bar => :xyzzy}, @request, @response)
 
322
      end
 
323
 
 
324
      it "should use the default status when a model search call succeeds" do
 
325
        @indirection.stubs(:search).returns(@result)
 
326
        @handler.do_search("my_handler", "my_result", {}, @request, @response)
 
327
      end
 
328
 
 
329
      it "should set the content type to the first format returned by the accept header" do
 
330
        @handler.expects(:accept_header).with(@request).returns "one,two"
 
331
        @handler.expects(:set_content_type).with(@response, @oneformat)
 
332
 
 
333
        @handler.do_search("my_handler", "my_result", {}, @request, @response)
 
334
      end
 
335
 
 
336
      it "should return a list of serialized objects when a model search call succeeds" do
 
337
        @handler.expects(:accept_header).with(@request).returns "one,two"
 
338
 
 
339
        @indirection.stubs(:search).returns(@result)
 
340
 
 
341
        @model_class.expects(:render_multiple).with(@oneformat, @result).returns "my rendered instances"
 
342
 
 
343
        @handler.expects(:set_response).with { |response, data| data == "my rendered instances" }
 
344
        @handler.do_search("my_handler", "my_result", {}, @request, @response)
 
345
      end
 
346
 
 
347
      it "should return [] when searching returns an empty array" do
 
348
        @handler.expects(:accept_header).with(@request).returns "one,two"
 
349
        @indirection.stubs(:search).returns([])
 
350
        @model_class.expects(:render_multiple).with(@oneformat, []).returns "[]"
 
351
 
 
352
 
 
353
        @handler.expects(:set_response).with { |response, data| data == "[]" }
 
354
        @handler.do_search("my_handler", "my_result", {}, @request, @response)
 
355
      end
 
356
 
 
357
      it "should return a 404 when searching returns nil" do
 
358
        @model_class.stubs(:name).returns "my name"
 
359
        @handler.expects(:set_response).with { |response, body, status| status == 404 }
 
360
        @indirection.stubs(:search).returns(nil)
 
361
        @handler.do_search("my_handler", "my_result", {}, @request, @response)
 
362
      end
 
363
    end
 
364
 
 
365
    describe "when destroying a model instance" do
 
366
      before do
 
367
        Puppet::Indirector::Indirection.expects(:instance).with(:my_handler).returns( stub "indirection", :model => @model_class )
 
368
 
 
369
        @result = stub 'result', :render => "the result"
 
370
        @indirection.stubs(:destroy).returns @result
 
371
      end
 
372
 
 
373
      it "should use the indirection request to find the model" do
 
374
        @handler.do_destroy("my_handler", "my_result", {}, @request, @response)
 
375
      end
 
376
 
 
377
      it "should use the escaped request key to destroy the instance in the model" do
 
378
        @indirection.expects(:destroy).with do |key, args|
 
379
          key == "foo bar"
 
380
        end
 
381
        @handler.do_destroy("my_handler", "foo bar", {}, @request, @response)
 
382
      end
 
383
 
 
384
      it "should use a common method for determining the request parameters" do
 
385
        @indirection.expects(:destroy).with do |key, args|
 
386
          args[:foo] == :baz and args[:bar] == :xyzzy
 
387
        end
 
388
        @handler.do_destroy("my_handler", "my_result", {:foo => :baz, :bar => :xyzzy}, @request, @response)
 
389
      end
 
390
 
 
391
      it "should use the default status code a model destroy call succeeds" do
 
392
        @handler.expects(:set_response).with { |response, body, status| status.nil? }
 
393
        @handler.do_destroy("my_handler", "my_result", {}, @request, @response)
 
394
      end
 
395
 
 
396
      it "should return a yaml-encoded result when a model destroy call succeeds" do
 
397
        @result = stub 'result', :to_yaml => "the result"
 
398
        @indirection.expects(:destroy).returns(@result)
 
399
 
 
400
        @handler.expects(:set_response).with { |response, body, status| body == "the result" }
 
401
 
 
402
        @handler.do_destroy("my_handler", "my_result", {}, @request, @response)
 
403
      end
 
404
    end
 
405
 
 
406
    describe "when saving a model instance" do
 
407
      before do
 
408
        Puppet::Indirector::Indirection.stubs(:instance).with(:my_handler).returns( stub "indirection", :model => @model_class )
 
409
        @handler.stubs(:body).returns('my stuff')
 
410
        @handler.stubs(:content_type_header).returns("text/yaml")
 
411
 
 
412
        @result = stub 'result', :render => "the result"
 
413
 
 
414
        @model_instance = stub('indirected model instance')
 
415
        @model_class.stubs(:convert_from).returns(@model_instance)
 
416
        @indirection.stubs(:save)
 
417
 
 
418
        @format = stub 'format', :suitable? => true, :name => "format", :mime => "text/format"
 
419
        Puppet::Network::FormatHandler.stubs(:format).returns @format
 
420
        @yamlformat = stub 'yaml', :suitable? => true, :name => "yaml", :mime => "text/yaml"
 
421
        Puppet::Network::FormatHandler.stubs(:format).with("yaml").returns @yamlformat
 
422
      end
 
423
 
 
424
      it "should use the indirection request to find the model" do
 
425
        @handler.do_save("my_handler", "my_result", {}, @request, @response)
 
426
      end
 
427
 
 
428
      it "should use the 'body' hook to retrieve the body of the request" do
 
429
        @handler.expects(:body).returns "my body"
 
430
        @model_class.expects(:convert_from).with { |format, body| body == "my body" }.returns @model_instance
 
431
 
 
432
        @handler.do_save("my_handler", "my_result", {}, @request, @response)
 
433
      end
 
434
 
 
435
      it "should fail to save model if data is not specified" do
 
436
        @handler.stubs(:body).returns('')
 
437
 
 
438
        lambda { @handler.do_save("my_handler", "my_result", {}, @request, @response) }.should raise_error(ArgumentError)
 
439
      end
 
440
 
 
441
      it "should use a common method for determining the request parameters" do
 
442
        @indirection.expects(:save).with(@model_instance, 'key').once
 
443
        @handler.do_save("my_handler", "key", {}, @request, @response)
 
444
      end
 
445
 
 
446
      it "should use the default status when a model save call succeeds" do
 
447
        @handler.expects(:set_response).with { |response, body, status| status.nil? }
 
448
        @handler.do_save("my_handler", "my_result", {}, @request, @response)
 
449
      end
 
450
 
 
451
      it "should return the yaml-serialized result when a model save call succeeds" do
 
452
        @indirection.stubs(:save).returns(@model_instance)
 
453
        @model_instance.expects(:to_yaml).returns('foo')
 
454
        @handler.do_save("my_handler", "my_result", {}, @request, @response)
 
455
      end
 
456
 
 
457
      it "should set the content to yaml" do
 
458
        @handler.expects(:set_content_type).with(@response, @yamlformat)
 
459
        @handler.do_save("my_handler", "my_result", {}, @request, @response)
 
460
      end
 
461
 
 
462
      it "should use the content-type header to know the body format" do
 
463
        @handler.expects(:content_type_header).returns("text/format")
 
464
        Puppet::Network::FormatHandler.stubs(:mime).with("text/format").returns @format
 
465
 
 
466
        @model_class.expects(:convert_from).with { |format, body| format == "format" }.returns @model_instance
 
467
 
 
468
        @handler.do_save("my_handler", "my_result", {}, @request, @response)
 
469
      end
 
470
    end
 
471
  end
 
472
 
 
473
  describe "when resolving node" do
 
474
    it "should use a look-up from the ip address" do
 
475
      Resolv.expects(:getname).with("1.2.3.4").returns("host.domain.com")
 
476
 
 
477
      @handler.resolve_node(:ip => "1.2.3.4")
 
478
    end
 
479
 
 
480
    it "should return the look-up result" do
 
481
      Resolv.stubs(:getname).with("1.2.3.4").returns("host.domain.com")
 
482
 
 
483
      @handler.resolve_node(:ip => "1.2.3.4").should == "host.domain.com"
 
484
    end
 
485
 
 
486
    it "should return the ip address if resolving fails" do
 
487
      Resolv.stubs(:getname).with("1.2.3.4").raises(RuntimeError, "no such host")
 
488
 
 
489
      @handler.resolve_node(:ip => "1.2.3.4").should == "1.2.3.4"
 
490
    end
 
491
  end
 
492
end