rack-protection 1.5.5 → 2.0.1
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.
- checksums.yaml +5 -5
- data/Gemfile +13 -0
- data/License +4 -1
- data/README.md +41 -13
- data/Rakefile +29 -5
- data/lib/rack/protection/authenticity_token.rb +107 -6
- data/lib/rack/protection/base.rb +2 -21
- data/lib/rack/protection/content_security_policy.rb +80 -0
- data/lib/rack/protection/cookie_tossing.rb +75 -0
- data/lib/rack/protection/escaped_params.rb +2 -0
- data/lib/rack/protection/form_token.rb +1 -1
- data/lib/rack/protection/http_origin.rb +8 -0
- data/lib/rack/protection/json_csrf.rb +26 -4
- data/lib/rack/protection/remote_token.rb +1 -1
- data/lib/rack/protection/strict_transport.rb +39 -0
- data/lib/rack/protection/version.rb +1 -12
- data/lib/rack/protection/xss_header.rb +1 -1
- data/lib/rack/protection.rb +38 -24
- data/rack-protection.gemspec +11 -104
- metadata +16 -80
- data/spec/authenticity_token_spec.rb +0 -48
- data/spec/base_spec.rb +0 -40
- data/spec/escaped_params_spec.rb +0 -43
- data/spec/form_token_spec.rb +0 -33
- data/spec/frame_options_spec.rb +0 -39
- data/spec/http_origin_spec.rb +0 -38
- data/spec/ip_spoofing_spec.rb +0 -35
- data/spec/json_csrf_spec.rb +0 -58
- data/spec/path_traversal_spec.rb +0 -41
- data/spec/protection_spec.rb +0 -105
- data/spec/remote_referrer_spec.rb +0 -31
- data/spec/remote_token_spec.rb +0 -42
- data/spec/session_hijacking_spec.rb +0 -55
- data/spec/spec_helper.rb +0 -163
- data/spec/xss_header_spec.rb +0 -56
data/spec/json_csrf_spec.rb
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
require File.expand_path('../spec_helper.rb', __FILE__)
|
|
2
|
-
|
|
3
|
-
describe Rack::Protection::JsonCsrf do
|
|
4
|
-
it_behaves_like "any rack application"
|
|
5
|
-
|
|
6
|
-
describe 'json response' do
|
|
7
|
-
before do
|
|
8
|
-
mock_app { |e| [200, {'Content-Type' => 'application/json'}, []]}
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
it "denies get requests with json responses with a remote referrer" do
|
|
12
|
-
get('/', {}, 'HTTP_REFERER' => 'http://evil.com').should_not be_ok
|
|
13
|
-
end
|
|
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
|
-
|
|
23
|
-
it "accepts get requests with json responses with a local referrer" do
|
|
24
|
-
get('/', {}, 'HTTP_REFERER' => '/').should be_ok
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
it "accepts get requests with json responses with no referrer" do
|
|
28
|
-
get('/', {}).should be_ok
|
|
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
|
-
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
describe 'not json response' do
|
|
38
|
-
|
|
39
|
-
it "accepts get requests with 304 headers" do
|
|
40
|
-
mock_app { |e| [304, {}, []]}
|
|
41
|
-
get('/', {}).status.should == 304
|
|
42
|
-
end
|
|
43
|
-
|
|
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
|
|
58
|
-
end
|
data/spec/path_traversal_spec.rb
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
require File.expand_path('../spec_helper.rb', __FILE__)
|
|
2
|
-
|
|
3
|
-
describe Rack::Protection::PathTraversal do
|
|
4
|
-
it_behaves_like "any rack application"
|
|
5
|
-
|
|
6
|
-
context 'escaping' do
|
|
7
|
-
before do
|
|
8
|
-
mock_app { |e| [200, {'Content-Type' => 'text/plain'}, [e['PATH_INFO']]] }
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
%w[/foo/bar /foo/bar/ / /.f /a.x].each do |path|
|
|
12
|
-
it("does not touch #{path.inspect}") { get(path).body.should == path }
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
{ # yes, this is ugly, feel free to change that
|
|
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'
|
|
19
|
-
}.each do |a, b|
|
|
20
|
-
it("replaces #{a.inspect} with #{b.inspect}") { get(a).body.should == b }
|
|
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
|
|
40
|
-
end
|
|
41
|
-
end
|
data/spec/protection_spec.rb
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
require File.expand_path('../spec_helper.rb', __FILE__)
|
|
2
|
-
|
|
3
|
-
describe Rack::Protection do
|
|
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
|
|
105
|
-
end
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
require File.expand_path('../spec_helper.rb', __FILE__)
|
|
2
|
-
|
|
3
|
-
describe Rack::Protection::RemoteReferrer do
|
|
4
|
-
it_behaves_like "any rack application"
|
|
5
|
-
|
|
6
|
-
it "accepts post requests with no referrer" do
|
|
7
|
-
post('/').should be_ok
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
it "does not accept post requests with no referrer if allow_empty_referrer is false" do
|
|
11
|
-
mock_app do
|
|
12
|
-
use Rack::Protection::RemoteReferrer, :allow_empty_referrer => false
|
|
13
|
-
run DummyApp
|
|
14
|
-
end
|
|
15
|
-
post('/').should_not be_ok
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
it "should allow post request with a relative referrer" do
|
|
19
|
-
post('/', {}, 'HTTP_REFERER' => '/').should be_ok
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
it "accepts post requests with the same host in the referrer" do
|
|
23
|
-
post('/', {}, 'HTTP_REFERER' => 'http://example.com/foo', 'HTTP_HOST' => 'example.com')
|
|
24
|
-
last_response.should be_ok
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
it "denies post requests with a remote referrer" do
|
|
28
|
-
post('/', {}, 'HTTP_REFERER' => 'http://example.com/foo', 'HTTP_HOST' => 'example.org')
|
|
29
|
-
last_response.should_not be_ok
|
|
30
|
-
end
|
|
31
|
-
end
|
data/spec/remote_token_spec.rb
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
require File.expand_path('../spec_helper.rb', __FILE__)
|
|
2
|
-
|
|
3
|
-
describe Rack::Protection::RemoteToken do
|
|
4
|
-
it_behaves_like "any rack application"
|
|
5
|
-
|
|
6
|
-
it "accepts post requests with no referrer" do
|
|
7
|
-
post('/').should be_ok
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
it "accepts post requests with a local referrer" do
|
|
11
|
-
post('/', {}, 'HTTP_REFERER' => '/').should be_ok
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
it "denies post requests with a remote referrer and no token" do
|
|
15
|
-
post('/', {}, 'HTTP_REFERER' => 'http://example.com/foo', 'HTTP_HOST' => 'example.org')
|
|
16
|
-
last_response.should_not be_ok
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
it "accepts post requests with a remote referrer and correct X-CSRF-Token header" do
|
|
20
|
-
post('/', {}, 'HTTP_REFERER' => 'http://example.com/foo', 'HTTP_HOST' => 'example.org',
|
|
21
|
-
'rack.session' => {:csrf => "a"}, 'HTTP_X_CSRF_TOKEN' => "a")
|
|
22
|
-
last_response.should be_ok
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
it "denies post requests with a remote referrer and wrong X-CSRF-Token header" do
|
|
26
|
-
post('/', {}, 'HTTP_REFERER' => 'http://example.com/foo', 'HTTP_HOST' => 'example.org',
|
|
27
|
-
'rack.session' => {:csrf => "a"}, 'HTTP_X_CSRF_TOKEN' => "b")
|
|
28
|
-
last_response.should_not be_ok
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
it "accepts post form requests with a remote referrer and correct authenticity_token field" do
|
|
32
|
-
post('/', {"authenticity_token" => "a"}, 'HTTP_REFERER' => 'http://example.com/foo',
|
|
33
|
-
'HTTP_HOST' => 'example.org', 'rack.session' => {:csrf => "a"})
|
|
34
|
-
last_response.should be_ok
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
it "denies post form requests with a remote referrer and wrong authenticity_token field" do
|
|
38
|
-
post('/', {"authenticity_token" => "a"}, 'HTTP_REFERER' => 'http://example.com/foo',
|
|
39
|
-
'HTTP_HOST' => 'example.org', 'rack.session' => {:csrf => "b"})
|
|
40
|
-
last_response.should_not be_ok
|
|
41
|
-
end
|
|
42
|
-
end
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
require File.expand_path('../spec_helper.rb', __FILE__)
|
|
2
|
-
|
|
3
|
-
describe Rack::Protection::SessionHijacking do
|
|
4
|
-
it_behaves_like "any rack application"
|
|
5
|
-
|
|
6
|
-
it "accepts a session without changes to tracked parameters" do
|
|
7
|
-
session = {:foo => :bar}
|
|
8
|
-
get '/', {}, 'rack.session' => session
|
|
9
|
-
get '/', {}, 'rack.session' => session
|
|
10
|
-
session[:foo].should == :bar
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
it "denies requests with a changing User-Agent header" do
|
|
14
|
-
session = {:foo => :bar}
|
|
15
|
-
get '/', {}, 'rack.session' => session, 'HTTP_USER_AGENT' => 'a'
|
|
16
|
-
get '/', {}, 'rack.session' => session, 'HTTP_USER_AGENT' => 'b'
|
|
17
|
-
session.should be_empty
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
it "accepts requests with a changing Accept-Encoding header" do
|
|
21
|
-
# this is tested because previously it led to clearing the session
|
|
22
|
-
session = {:foo => :bar}
|
|
23
|
-
get '/', {}, 'rack.session' => session, 'HTTP_ACCEPT_ENCODING' => 'a'
|
|
24
|
-
get '/', {}, 'rack.session' => session, 'HTTP_ACCEPT_ENCODING' => 'b'
|
|
25
|
-
session.should_not be_empty
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
it "denies requests with a changing Accept-Language header" do
|
|
29
|
-
session = {:foo => :bar}
|
|
30
|
-
get '/', {}, 'rack.session' => session, 'HTTP_ACCEPT_LANGUAGE' => 'a'
|
|
31
|
-
get '/', {}, 'rack.session' => session, 'HTTP_ACCEPT_LANGUAGE' => 'b'
|
|
32
|
-
session.should be_empty
|
|
33
|
-
end
|
|
34
|
-
|
|
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
|
|
50
|
-
session = {:foo => :bar}
|
|
51
|
-
get '/', {}, 'rack.session' => session, 'HTTP_VERSION' => '1.0'
|
|
52
|
-
get '/', {}, 'rack.session' => session, 'HTTP_VERSION' => '1.1'
|
|
53
|
-
session[:foo].should == :bar
|
|
54
|
-
end
|
|
55
|
-
end
|
data/spec/spec_helper.rb
DELETED
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
require 'rack/protection'
|
|
2
|
-
require 'rack/test'
|
|
3
|
-
require 'rack'
|
|
4
|
-
require 'forwardable'
|
|
5
|
-
require 'stringio'
|
|
6
|
-
|
|
7
|
-
if defined? Gem.loaded_specs and Gem.loaded_specs.include? 'rack'
|
|
8
|
-
version = Gem.loaded_specs['rack'].version.to_s
|
|
9
|
-
else
|
|
10
|
-
version = Rack.release + '.0'
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
if version == "1.3"
|
|
14
|
-
Rack::Session::Abstract::ID.class_eval do
|
|
15
|
-
private
|
|
16
|
-
def prepare_session(env)
|
|
17
|
-
session_was = env[ENV_SESSION_KEY]
|
|
18
|
-
env[ENV_SESSION_KEY] = SessionHash.new(self, env)
|
|
19
|
-
env[ENV_SESSION_OPTIONS_KEY] = OptionsHash.new(self, env, @default_options)
|
|
20
|
-
env[ENV_SESSION_KEY].merge! session_was if session_was
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
unless Rack::MockResponse.method_defined? :header
|
|
26
|
-
Rack::MockResponse.send(:alias_method, :header, :headers)
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
module DummyApp
|
|
30
|
-
def self.call(env)
|
|
31
|
-
Thread.current[:last_env] = env
|
|
32
|
-
body = (env['REQUEST_METHOD'] == 'HEAD' ? '' : 'ok')
|
|
33
|
-
[200, {'Content-Type' => env['wants'] || 'text/plain'}, [body]]
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
module TestHelpers
|
|
38
|
-
extend Forwardable
|
|
39
|
-
def_delegators :last_response, :body, :headers, :status, :errors
|
|
40
|
-
def_delegators :current_session, :env_for
|
|
41
|
-
attr_writer :app
|
|
42
|
-
|
|
43
|
-
def app
|
|
44
|
-
@app || mock_app(DummyApp)
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def mock_app(app = nil, &block)
|
|
48
|
-
app = block if app.nil? and block.arity == 1
|
|
49
|
-
if app
|
|
50
|
-
klass = described_class
|
|
51
|
-
mock_app do
|
|
52
|
-
use Rack::Head
|
|
53
|
-
use(Rack::Config) { |e| e['rack.session'] ||= {}}
|
|
54
|
-
use klass
|
|
55
|
-
run app
|
|
56
|
-
end
|
|
57
|
-
else
|
|
58
|
-
@app = Rack::Lint.new Rack::Builder.new(&block).to_app
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def with_headers(headers)
|
|
63
|
-
proc { [200, {'Content-Type' => 'text/plain'}.merge(headers), ['ok']] }
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def env
|
|
67
|
-
Thread.current[:last_env]
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
# see http://blog.101ideas.cz/posts/pending-examples-via-not-implemented-error-in-rspec.html
|
|
72
|
-
module NotImplementedAsPending
|
|
73
|
-
def self.included(base)
|
|
74
|
-
base.class_eval do
|
|
75
|
-
alias_method :__finish__, :finish
|
|
76
|
-
remove_method :finish
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def finish(reporter)
|
|
81
|
-
if @exception.is_a?(NotImplementedError)
|
|
82
|
-
from = @exception.backtrace[0]
|
|
83
|
-
message = "#{@exception.message} (from #{from})"
|
|
84
|
-
@pending_declared_in_example = message
|
|
85
|
-
metadata[:pending] = true
|
|
86
|
-
@exception = nil
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
__finish__(reporter)
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
RSpec::Core::Example.send :include, self
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
RSpec.configure do |config|
|
|
96
|
-
config.expect_with :rspec, :stdlib
|
|
97
|
-
config.include Rack::Test::Methods
|
|
98
|
-
config.include TestHelpers
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
shared_examples_for 'any rack application' do
|
|
102
|
-
it "should not interfere with normal get requests" do
|
|
103
|
-
get('/').should be_ok
|
|
104
|
-
body.should == 'ok'
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
it "should not interfere with normal head requests" do
|
|
108
|
-
head('/').should be_ok
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
it 'should not leak changes to env' do
|
|
112
|
-
klass = described_class
|
|
113
|
-
detector = Struct.new(:app)
|
|
114
|
-
|
|
115
|
-
detector.send(:define_method, :call) do |env|
|
|
116
|
-
was = env.dup
|
|
117
|
-
res = app.call(env)
|
|
118
|
-
was.each do |k,v|
|
|
119
|
-
next if env[k] == v
|
|
120
|
-
fail "env[#{k.inspect}] changed from #{v.inspect} to #{env[k].inspect}"
|
|
121
|
-
end
|
|
122
|
-
res
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
mock_app do
|
|
126
|
-
use Rack::Head
|
|
127
|
-
use(Rack::Config) { |e| e['rack.session'] ||= {}}
|
|
128
|
-
use detector
|
|
129
|
-
use klass
|
|
130
|
-
run DummyApp
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
get('/..', :foo => '<bar>').should be_ok
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
it 'allows passing on values in env' do
|
|
137
|
-
klass = described_class
|
|
138
|
-
detector = Struct.new(:app)
|
|
139
|
-
changer = Struct.new(:app)
|
|
140
|
-
|
|
141
|
-
detector.send(:define_method, :call) do |env|
|
|
142
|
-
res = app.call(env)
|
|
143
|
-
env['foo.bar'].should == 42
|
|
144
|
-
res
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
changer.send(:define_method, :call) do |env|
|
|
148
|
-
env['foo.bar'] = 42
|
|
149
|
-
app.call(env)
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
mock_app do
|
|
153
|
-
use Rack::Head
|
|
154
|
-
use(Rack::Config) { |e| e['rack.session'] ||= {}}
|
|
155
|
-
use detector
|
|
156
|
-
use klass
|
|
157
|
-
use changer
|
|
158
|
-
run DummyApp
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
get('/').should be_ok
|
|
162
|
-
end
|
|
163
|
-
end
|
data/spec/xss_header_spec.rb
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
require File.expand_path('../spec_helper.rb', __FILE__)
|
|
2
|
-
|
|
3
|
-
describe Rack::Protection::XSSHeader do
|
|
4
|
-
it_behaves_like "any rack application"
|
|
5
|
-
|
|
6
|
-
it 'should set the X-XSS-Protection' do
|
|
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
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
it 'should allow changing the protection mode' do
|
|
19
|
-
# I have no clue what other modes are available
|
|
20
|
-
mock_app do
|
|
21
|
-
use Rack::Protection::XSSHeader, :xss_mode => :foo
|
|
22
|
-
run DummyApp
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
get('/', {}, 'wants' => 'application/xhtml').headers["X-XSS-Protection"].should == "1; mode=foo"
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
it 'should not override the header if already set' do
|
|
29
|
-
mock_app with_headers("X-XSS-Protection" => "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"
|
|
55
|
-
end
|
|
56
|
-
end
|