rack-protection 1.0.0 → 1.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,23 +2,74 @@
2
2
  Gem::Specification.new do |s|
3
3
  # general infos
4
4
  s.name = "rack-protection"
5
- s.version = "1.0.0"
5
+ s.version = "1.5.5"
6
6
  s.description = "You should use protection!"
7
7
  s.homepage = "http://github.com/rkh/rack-protection"
8
8
  s.summary = s.description
9
+ s.license = 'MIT'
9
10
 
10
11
  # generated from git shortlog -sn
11
12
  s.authors = [
12
13
  "Konstantin Haase",
14
+ "Alex Rodionov",
15
+ "Patrick Ellis",
16
+ "Jason Staten",
17
+ "ITO Nobuaki",
18
+ "Jeff Welling",
19
+ "Matteo Centenaro",
20
+ "Egor Homakov",
21
+ "Florian Gilcher",
22
+ "Fojas",
23
+ "Igor Bochkariov",
24
+ "Mael Clerambault",
25
+ "Martin Mauch",
26
+ "Renne Nissinen",
27
+ "SAKAI, Kazuaki",
28
+ "Stanislav Savulchik",
29
+ "Steve Agalloco",
30
+ "TOBY",
31
+ "Thais Camilo and Konstantin Haase",
32
+ "Vipul A M",
33
+ "Akzhan Abdulin",
34
+ "brookemckim",
35
+ "Bj\u{f8}rge N\u{e6}ss",
36
+ "Chris Heald",
37
+ "Chris Mytton",
13
38
  "Corey Ward",
14
- "Fojas"
39
+ "Dario Cravero",
40
+ "David Kellum"
15
41
  ]
16
42
 
17
43
  # generated from git shortlog -sne
18
44
  s.email = [
19
45
  "konstantin.mailinglists@googlemail.com",
46
+ "p0deje@gmail.com",
47
+ "jstaten07@gmail.com",
48
+ "patrick@soundcloud.com",
49
+ "jeff.welling@gmail.com",
50
+ "bugant@gmail.com",
51
+ "daydream.trippers@gmail.com",
52
+ "florian.gilcher@asquera.de",
53
+ "developer@fojasaur.us",
54
+ "ujifgc@gmail.com",
55
+ "mael@clerambault.fr",
56
+ "martin.mauch@gmail.com",
57
+ "rennex@iki.fi",
58
+ "kaz.july.7@gmail.com",
59
+ "s.savulchik@gmail.com",
60
+ "steve.agalloco@gmail.com",
61
+ "toby.net.info.mail+git@gmail.com",
62
+ "dev+narwen+rkh@rkh.im",
63
+ "vipulnsward@gmail.com",
64
+ "akzhan.abdulin@gmail.com",
65
+ "brooke@digitalocean.com",
66
+ "bjoerge@bengler.no",
67
+ "cheald@gmail.com",
68
+ "self@hecticjeff.net",
20
69
  "coreyward@me.com",
21
- "developer@fojasaur.us"
70
+ "dario@uxtemple.com",
71
+ "dek-oss@gravitext.com",
72
+ "homakov@gmail.com"
22
73
  ]
23
74
 
24
75
  # generated from git ls-files
@@ -33,6 +84,7 @@ Gem::Specification.new do |s|
33
84
  "lib/rack/protection/escaped_params.rb",
34
85
  "lib/rack/protection/form_token.rb",
35
86
  "lib/rack/protection/frame_options.rb",
87
+ "lib/rack/protection/http_origin.rb",
36
88
  "lib/rack/protection/ip_spoofing.rb",
37
89
  "lib/rack/protection/json_csrf.rb",
38
90
  "lib/rack/protection/path_traversal.rb",
@@ -43,9 +95,11 @@ Gem::Specification.new do |s|
43
95
  "lib/rack/protection/xss_header.rb",
44
96
  "rack-protection.gemspec",
45
97
  "spec/authenticity_token_spec.rb",
98
+ "spec/base_spec.rb",
46
99
  "spec/escaped_params_spec.rb",
47
100
  "spec/form_token_spec.rb",
48
101
  "spec/frame_options_spec.rb",
102
+ "spec/http_origin_spec.rb",
49
103
  "spec/ip_spoofing_spec.rb",
50
104
  "spec/json_csrf_spec.rb",
51
105
  "spec/path_traversal_spec.rb",
@@ -59,7 +113,6 @@ Gem::Specification.new do |s|
59
113
 
60
114
  # dependencies
61
115
  s.add_dependency "rack"
62
- s.add_dependency "escape_utils"
63
116
  s.add_development_dependency "rack-test"
64
117
  s.add_development_dependency "rspec", "~> 2.0"
65
118
  end
@@ -30,4 +30,19 @@ describe Rack::Protection::AuthenticityToken do
30
30
  it "prevents ajax requests without a valid token" do
31
31
  post('/', {}, "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest").should_not be_ok
32
32
  end
33
+
34
+ it "allows for a custom authenticity token param" do
35
+ mock_app do
36
+ use Rack::Protection::AuthenticityToken, :authenticity_param => 'csrf_param'
37
+ run proc { |e| [200, {'Content-Type' => 'text/plain'}, ['hi']] }
38
+ end
39
+
40
+ post('/', {"csrf_param" => "a"}, 'rack.session' => {:csrf => "a"})
41
+ last_response.should be_ok
42
+ end
43
+
44
+ it "sets a new csrf token for the session in env, even after a 'safe' request" do
45
+ get('/', {}, {})
46
+ env['rack.session'][:csrf].should_not be_nil
47
+ end
33
48
  end
data/spec/base_spec.rb ADDED
@@ -0,0 +1,40 @@
1
+ require File.expand_path('../spec_helper.rb', __FILE__)
2
+
3
+ describe Rack::Protection::Base do
4
+
5
+ subject { described_class.new(lambda {}) }
6
+
7
+ describe "#random_string" do
8
+ it "outputs a string of 32 characters" do
9
+ subject.random_string.length.should == 32
10
+ end
11
+ end
12
+
13
+ describe "#referrer" do
14
+ it "Reads referrer from Referer header" do
15
+ env = {"HTTP_HOST" => "foo.com", "HTTP_REFERER" => "http://bar.com/valid"}
16
+ subject.referrer(env).should == "bar.com"
17
+ end
18
+
19
+ it "Reads referrer from Host header when Referer header is relative" do
20
+ env = {"HTTP_HOST" => "foo.com", "HTTP_REFERER" => "/valid"}
21
+ subject.referrer(env).should == "foo.com"
22
+ end
23
+
24
+ it "Reads referrer from Host header when Referer header is missing" do
25
+ env = {"HTTP_HOST" => "foo.com"}
26
+ subject.referrer(env).should == "foo.com"
27
+ end
28
+
29
+ it "Returns nil when Referer header is missing and allow_empty_referrer is false" do
30
+ env = {"HTTP_HOST" => "foo.com"}
31
+ subject.options[:allow_empty_referrer] = false
32
+ subject.referrer(env).should be_nil
33
+ end
34
+
35
+ it "Returns nil when Referer header is invalid" do
36
+ env = {"HTTP_HOST" => "foo.com", "HTTP_REFERER" => "http://bar.com/bad|uri"}
37
+ subject.referrer(env).should be_nil
38
+ end
39
+ end
40
+ end
@@ -30,5 +30,14 @@ describe Rack::Protection::EscapedParams do
30
30
  get '/', :foo => {:bar => "<bar>"}
31
31
  body.should == '&lt;bar&gt;'
32
32
  end
33
+
34
+ it 'leaves cache-breaker params untouched' do
35
+ mock_app do |env|
36
+ [200, {'Content-Type' => 'text/plain'}, ['hi']]
37
+ end
38
+
39
+ get '/?95df8d9bf5237ad08df3115ee74dcb10'
40
+ body.should == 'hi'
41
+ end
33
42
  end
34
43
  end
@@ -3,8 +3,12 @@ require File.expand_path('../spec_helper.rb', __FILE__)
3
3
  describe Rack::Protection::FrameOptions do
4
4
  it_behaves_like "any rack application"
5
5
 
6
- it 'should set the X-XSS-Protection' do
7
- get('/').headers["X-Frame-Options"].should == "sameorigin"
6
+ it 'should set the X-Frame-Options' do
7
+ get('/', {}, 'wants' => 'text/html').headers["X-Frame-Options"].should == "SAMEORIGIN"
8
+ end
9
+
10
+ it 'should not set the X-Frame-Options for other content types' do
11
+ get('/', {}, 'wants' => 'text/foo').headers["X-Frame-Options"].should be_nil
8
12
  end
9
13
 
10
14
  it 'should allow changing the protection mode' do
@@ -14,11 +18,22 @@ describe Rack::Protection::FrameOptions do
14
18
  run DummyApp
15
19
  end
16
20
 
17
- get('/').headers["X-Frame-Options"].should == "deny"
21
+ get('/', {}, 'wants' => 'text/html').headers["X-Frame-Options"].should == "DENY"
22
+ end
23
+
24
+
25
+ it 'should allow changing the protection mode to a string' do
26
+ # I have no clue what other modes are available
27
+ mock_app do
28
+ use Rack::Protection::FrameOptions, :frame_options => "ALLOW-FROM foo"
29
+ run DummyApp
30
+ end
31
+
32
+ get('/', {}, 'wants' => 'text/html').headers["X-Frame-Options"].should == "ALLOW-FROM foo"
18
33
  end
19
34
 
20
35
  it 'should not override the header if already set' do
21
36
  mock_app with_headers("X-Frame-Options" => "allow")
22
- get('/').headers["X-Frame-Options"].should == "allow"
37
+ get('/', {}, 'wants' => 'text/html').headers["X-Frame-Options"].should == "allow"
23
38
  end
24
39
  end
@@ -0,0 +1,38 @@
1
+ require File.expand_path('../spec_helper.rb', __FILE__)
2
+
3
+ describe Rack::Protection::HttpOrigin do
4
+ it_behaves_like "any rack application"
5
+
6
+ before(:each) do
7
+ mock_app do
8
+ use Rack::Protection::HttpOrigin
9
+ run DummyApp
10
+ end
11
+ end
12
+
13
+ %w(GET HEAD POST PUT DELETE).each do |method|
14
+ it "accepts #{method} requests with no Origin" do
15
+ send(method.downcase, '/').should be_ok
16
+ end
17
+ end
18
+
19
+ %w(GET HEAD).each do |method|
20
+ it "accepts #{method} requests with non-whitelisted Origin" do
21
+ send(method.downcase, '/', {}, 'HTTP_ORIGIN' => 'http://malicious.com').should be_ok
22
+ end
23
+ end
24
+
25
+ %w(POST PUT DELETE).each do |method|
26
+ it "denies #{method} requests with non-whitelisted Origin" do
27
+ send(method.downcase, '/', {}, 'HTTP_ORIGIN' => 'http://malicious.com').should_not be_ok
28
+ end
29
+
30
+ it "accepts #{method} requests with whitelisted Origin" do
31
+ mock_app do
32
+ use Rack::Protection::HttpOrigin, :origin_whitelist => ['http://www.friend.com']
33
+ run DummyApp
34
+ end
35
+ send(method.downcase, '/', {}, 'HTTP_ORIGIN' => 'http://www.friend.com').should be_ok
36
+ end
37
+ end
38
+ end
@@ -12,6 +12,14 @@ describe Rack::Protection::JsonCsrf do
12
12
  get('/', {}, 'HTTP_REFERER' => 'http://evil.com').should_not be_ok
13
13
  end
14
14
 
15
+ it "accepts requests with json responses with a remote referrer when there's an origin header set" do
16
+ get('/', {}, 'HTTP_REFERER' => 'http://good.com', 'HTTP_ORIGIN' => 'http://good.com').should be_ok
17
+ end
18
+
19
+ it "accepts requests with json responses with a remote referrer when there's an x-origin header set" do
20
+ get('/', {}, 'HTTP_REFERER' => 'http://good.com', 'HTTP_X_ORIGIN' => 'http://good.com').should be_ok
21
+ end
22
+
15
23
  it "accepts get requests with json responses with a local referrer" do
16
24
  get('/', {}, 'HTTP_REFERER' => '/').should be_ok
17
25
  end
@@ -19,6 +27,11 @@ describe Rack::Protection::JsonCsrf do
19
27
  it "accepts get requests with json responses with no referrer" do
20
28
  get('/', {}).should be_ok
21
29
  end
30
+
31
+ it "accepts XHR requests" do
32
+ get('/', {}, 'HTTP_REFERER' => 'http://evil.com', 'HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest').should be_ok
33
+ end
34
+
22
35
  end
23
36
 
24
37
  describe 'not json response' do
@@ -29,4 +42,17 @@ describe Rack::Protection::JsonCsrf do
29
42
  end
30
43
 
31
44
  end
45
+
46
+ describe 'with drop_session as default reaction' do
47
+ it 'still denies' do
48
+ mock_app do
49
+ use Rack::Protection, :reaction => :drop_session
50
+ run proc { |e| [200, {'Content-Type' => 'application/json'}, []]}
51
+ end
52
+
53
+ session = {:foo => :bar}
54
+ get('/', {}, 'HTTP_REFERER' => 'http://evil.com', 'rack.session' => session)
55
+ last_response.should_not be_ok
56
+ end
57
+ end
32
58
  end
@@ -14,10 +14,28 @@ describe Rack::Protection::PathTraversal do
14
14
 
15
15
  { # yes, this is ugly, feel free to change that
16
16
  '/..' => '/', '/a/../b' => '/b', '/a/../b/' => '/b/', '/a/.' => '/a/',
17
- '/%2e.' => '/', '/a/%2e%2e/b' => '/b', '/a%2f%2e%2e%2fb/' => '/b/',
18
- '//' => '/', '/%2fetc%2fpasswd' => '/etc/passwd'
17
+ '/%2e.' => '/', '/a/%2E%2e/b' => '/b', '/a%2f%2E%2e%2Fb/' => '/b/',
18
+ '//' => '/', '/%2fetc%2Fpasswd' => '/etc/passwd'
19
19
  }.each do |a, b|
20
20
  it("replaces #{a.inspect} with #{b.inspect}") { get(a).body.should == b }
21
21
  end
22
+
23
+ it 'should be able to deal with PATH_INFO = nil (fcgi?)' do
24
+ app = Rack::Protection::PathTraversal.new(proc { 42 })
25
+ app.call({}).should be == 42
26
+ end
27
+ end
28
+
29
+ if "".respond_to?(:encoding) # Ruby 1.9+ M17N
30
+ context "PATH_INFO's encoding" do
31
+ before do
32
+ @app = Rack::Protection::PathTraversal.new(proc { |e| [200, {'Content-Type' => 'text/plain'}, [e['PATH_INFO'].encoding.to_s]] })
33
+ end
34
+
35
+ it 'should remain unchanged as ASCII-8BIT' do
36
+ body = @app.call({ 'PATH_INFO' => '/'.encode('ASCII-8BIT') })[2][0]
37
+ body.should == 'ASCII-8BIT'
38
+ end
39
+ end
22
40
  end
23
41
  end
@@ -2,4 +2,104 @@ require File.expand_path('../spec_helper.rb', __FILE__)
2
2
 
3
3
  describe Rack::Protection do
4
4
  it_behaves_like "any rack application"
5
+
6
+ it 'passes on options' do
7
+ mock_app do
8
+ use Rack::Protection, :track => ['HTTP_FOO']
9
+ run proc { |e| [200, {'Content-Type' => 'text/plain'}, ['hi']] }
10
+ end
11
+
12
+ session = {:foo => :bar}
13
+ get '/', {}, 'rack.session' => session, 'HTTP_ACCEPT_ENCODING' => 'a'
14
+ get '/', {}, 'rack.session' => session, 'HTTP_ACCEPT_ENCODING' => 'b'
15
+ session[:foo].should be == :bar
16
+
17
+ get '/', {}, 'rack.session' => session, 'HTTP_FOO' => 'BAR'
18
+ session.should be_empty
19
+ end
20
+
21
+ it 'passes errors through if :reaction => :report is used' do
22
+ mock_app do
23
+ use Rack::Protection, :reaction => :report
24
+ run proc { |e| [200, {'Content-Type' => 'text/plain'}, [e["protection.failed"].to_s]] }
25
+ end
26
+
27
+ session = {:foo => :bar}
28
+ post('/', {}, 'rack.session' => session, 'HTTP_ORIGIN' => 'http://malicious.com')
29
+ last_response.should be_ok
30
+ body.should == "true"
31
+ end
32
+
33
+ describe "#react" do
34
+ it 'prevents attacks and warns about it' do
35
+ io = StringIO.new
36
+ mock_app do
37
+ use Rack::Protection, :logger => Logger.new(io)
38
+ run DummyApp
39
+ end
40
+ post('/', {}, 'rack.session' => {}, 'HTTP_ORIGIN' => 'http://malicious.com')
41
+ io.string.should match /prevented.*Origin/
42
+ end
43
+
44
+ it 'reports attacks if reaction is to report' do
45
+ io = StringIO.new
46
+ mock_app do
47
+ use Rack::Protection, :reaction => :report, :logger => Logger.new(io)
48
+ run DummyApp
49
+ end
50
+ post('/', {}, 'rack.session' => {}, 'HTTP_ORIGIN' => 'http://malicious.com')
51
+ io.string.should match /reported.*Origin/
52
+ io.string.should_not match /prevented.*Origin/
53
+ end
54
+
55
+ it 'passes errors to reaction method if specified' do
56
+ io = StringIO.new
57
+ Rack::Protection::Base.send(:define_method, :special) { |*args| io << args.inspect }
58
+ mock_app do
59
+ use Rack::Protection, :reaction => :special, :logger => Logger.new(io)
60
+ run DummyApp
61
+ end
62
+ post('/', {}, 'rack.session' => {}, 'HTTP_ORIGIN' => 'http://malicious.com')
63
+ io.string.should match /HTTP_ORIGIN.*malicious.com/
64
+ io.string.should_not match /reported|prevented/
65
+ end
66
+ end
67
+
68
+ describe "#html?" do
69
+ context "given an appropriate content-type header" do
70
+ subject { Rack::Protection::Base.new(nil).html? 'content-type' => "text/html" }
71
+ it { should be_true }
72
+ end
73
+
74
+ context "given an inappropriate content-type header" do
75
+ subject { Rack::Protection::Base.new(nil).html? 'content-type' => "image/gif" }
76
+ it { should be_false }
77
+ end
78
+
79
+ context "given no content-type header" do
80
+ subject { Rack::Protection::Base.new(nil).html?({}) }
81
+ it { should be_false }
82
+ end
83
+ end
84
+
85
+ describe "#instrument" do
86
+ let(:env) { { 'rack.protection.attack' => 'base' } }
87
+ let(:instrumenter) { double('Instrumenter') }
88
+
89
+ after do
90
+ app.instrument(env)
91
+ end
92
+
93
+ context 'with an instrumenter specified' do
94
+ let(:app) { Rack::Protection::Base.new(nil, :instrumenter => instrumenter) }
95
+
96
+ it { instrumenter.should_receive(:instrument).with('rack.protection', env) }
97
+ end
98
+
99
+ context 'with no instrumenter specified' do
100
+ let(:app) { Rack::Protection::Base.new(nil) }
101
+
102
+ it { instrumenter.should_not_receive(:instrument) }
103
+ end
104
+ end
5
105
  end
@@ -17,11 +17,12 @@ describe Rack::Protection::SessionHijacking do
17
17
  session.should be_empty
18
18
  end
19
19
 
20
- it "denies requests with a changing Accept-Encoding header" do
20
+ it "accepts requests with a changing Accept-Encoding header" do
21
+ # this is tested because previously it led to clearing the session
21
22
  session = {:foo => :bar}
22
23
  get '/', {}, 'rack.session' => session, 'HTTP_ACCEPT_ENCODING' => 'a'
23
24
  get '/', {}, 'rack.session' => session, 'HTTP_ACCEPT_ENCODING' => 'b'
24
- session.should be_empty
25
+ session.should_not be_empty
25
26
  end
26
27
 
27
28
  it "denies requests with a changing Accept-Language header" do
@@ -31,10 +32,24 @@ describe Rack::Protection::SessionHijacking do
31
32
  session.should be_empty
32
33
  end
33
34
 
34
- it "denies requests with a changing Version header"do
35
+ it "accepts requests with the same Accept-Language header" do
36
+ session = {:foo => :bar}
37
+ get '/', {}, 'rack.session' => session, 'HTTP_ACCEPT_LANGUAGE' => 'a'
38
+ get '/', {}, 'rack.session' => session, 'HTTP_ACCEPT_LANGUAGE' => 'a'
39
+ session.should_not be_empty
40
+ end
41
+
42
+ it "comparison of Accept-Language header is not case sensitive" do
43
+ session = {:foo => :bar}
44
+ get '/', {}, 'rack.session' => session, 'HTTP_ACCEPT_LANGUAGE' => 'a'
45
+ get '/', {}, 'rack.session' => session, 'HTTP_ACCEPT_LANGUAGE' => 'A'
46
+ session.should_not be_empty
47
+ end
48
+
49
+ it "accepts requests with a changing Version header"do
35
50
  session = {:foo => :bar}
36
51
  get '/', {}, 'rack.session' => session, 'HTTP_VERSION' => '1.0'
37
52
  get '/', {}, 'rack.session' => session, 'HTTP_VERSION' => '1.1'
38
- session.should be_empty
53
+ session[:foo].should == :bar
39
54
  end
40
55
  end
data/spec/spec_helper.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'rack/protection'
2
2
  require 'rack/test'
3
+ require 'rack'
3
4
  require 'forwardable'
4
5
  require 'stringio'
5
6
 
@@ -21,10 +22,15 @@ if version == "1.3"
21
22
  end
22
23
  end
23
24
 
25
+ unless Rack::MockResponse.method_defined? :header
26
+ Rack::MockResponse.send(:alias_method, :header, :headers)
27
+ end
28
+
24
29
  module DummyApp
25
30
  def self.call(env)
26
31
  Thread.current[:last_env] = env
27
- [200, {'Content-Type' => 'text/plain'}, ['ok']]
32
+ body = (env['REQUEST_METHOD'] == 'HEAD' ? '' : 'ok')
33
+ [200, {'Content-Type' => env['wants'] || 'text/plain'}, [body]]
28
34
  end
29
35
  end
30
36
 
@@ -4,7 +4,15 @@ describe Rack::Protection::XSSHeader do
4
4
  it_behaves_like "any rack application"
5
5
 
6
6
  it 'should set the X-XSS-Protection' do
7
- get('/').headers["X-XSS-Protection"].should == "1; mode=block"
7
+ get('/', {}, 'wants' => 'text/html;charset=utf-8').headers["X-XSS-Protection"].should == "1; mode=block"
8
+ end
9
+
10
+ it 'should set the X-XSS-Protection for XHTML' do
11
+ get('/', {}, 'wants' => 'application/xhtml+xml').headers["X-XSS-Protection"].should == "1; mode=block"
12
+ end
13
+
14
+ it 'should not set the X-XSS-Protection for other content types' do
15
+ get('/', {}, 'wants' => 'application/foo').headers["X-XSS-Protection"].should be_nil
8
16
  end
9
17
 
10
18
  it 'should allow changing the protection mode' do
@@ -14,11 +22,35 @@ describe Rack::Protection::XSSHeader do
14
22
  run DummyApp
15
23
  end
16
24
 
17
- get('/').headers["X-XSS-Protection"].should == "1; mode=foo"
25
+ get('/', {}, 'wants' => 'application/xhtml').headers["X-XSS-Protection"].should == "1; mode=foo"
18
26
  end
19
27
 
20
28
  it 'should not override the header if already set' do
21
29
  mock_app with_headers("X-XSS-Protection" => "0")
22
- get('/').headers["X-XSS-Protection"].should == "0"
30
+ get('/', {}, 'wants' => 'text/html').headers["X-XSS-Protection"].should == "0"
31
+ end
32
+
33
+ it 'should set the X-Content-Type-Options' do
34
+ get('/', {}, 'wants' => 'text/html').header["X-Content-Type-Options"].should == "nosniff"
35
+ end
36
+
37
+
38
+ it 'should set the X-Content-Type-Options for other content types' do
39
+ get('/', {}, 'wants' => 'application/foo').header["X-Content-Type-Options"].should == "nosniff"
40
+ end
41
+
42
+
43
+ it 'should allow changing the nosniff-mode off' do
44
+ mock_app do
45
+ use Rack::Protection::XSSHeader, :nosniff => false
46
+ run DummyApp
47
+ end
48
+
49
+ get('/').headers["X-Content-Type-Options"].should be_nil
50
+ end
51
+
52
+ it 'should not override the header if already set X-Content-Type-Options' do
53
+ mock_app with_headers("X-Content-Type-Options" => "sniff")
54
+ get('/', {}, 'wants' => 'text/html').headers["X-Content-Type-Options"].should == "sniff"
23
55
  end
24
56
  end