rack-protection 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rack-protection might be problematic. Click here for more details.
- data/License +20 -0
- data/README.md +80 -0
- data/Rakefile +37 -0
- data/lib/rack-protection.rb +1 -0
- data/lib/rack/protection.rb +35 -0
- data/lib/rack/protection/authenticity_token.rb +24 -0
- data/lib/rack/protection/base.rb +97 -0
- data/lib/rack/protection/escaped_params.rb +61 -0
- data/lib/rack/protection/form_token.rb +23 -0
- data/lib/rack/protection/frame_options.rb +26 -0
- data/lib/rack/protection/ip_spoofing.rb +23 -0
- data/lib/rack/protection/json_csrf.rb +25 -0
- data/lib/rack/protection/path_traversal.rb +29 -0
- data/lib/rack/protection/remote_referrer.rb +23 -0
- data/lib/rack/protection/remote_token.rb +22 -0
- data/lib/rack/protection/session_hijacking.rb +36 -0
- data/lib/rack/protection/version.rb +44 -0
- data/lib/rack/protection/xss_header.rb +27 -0
- data/rack-protection.gemspec +61 -0
- data/spec/authenticity_token_spec.rb +33 -0
- data/spec/escaped_params_spec.rb +34 -0
- data/spec/form_token_spec.rb +33 -0
- data/spec/frame_options_spec.rb +24 -0
- data/spec/ip_spoofing_spec.rb +35 -0
- data/spec/json_csrf_spec.rb +23 -0
- data/spec/path_traversal_spec.rb +23 -0
- data/spec/protection_spec.rb +5 -0
- data/spec/remote_referrer_spec.rb +31 -0
- data/spec/remote_token_spec.rb +42 -0
- data/spec/session_hijacking_spec.rb +40 -0
- data/spec/spec_helper.rb +157 -0
- data/spec/xss_header_spec.rb +24 -0
- metadata +121 -0
@@ -0,0 +1,31 @@
|
|
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
|
@@ -0,0 +1,42 @@
|
|
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
|
@@ -0,0 +1,40 @@
|
|
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 "denies requests with a changing Accept-Encoding header" do
|
21
|
+
session = {:foo => :bar}
|
22
|
+
get '/', {}, 'rack.session' => session, 'HTTP_ACCEPT_ENCODING' => 'a'
|
23
|
+
get '/', {}, 'rack.session' => session, 'HTTP_ACCEPT_ENCODING' => 'b'
|
24
|
+
session.should be_empty
|
25
|
+
end
|
26
|
+
|
27
|
+
it "denies requests with a changing Accept-Language header" do
|
28
|
+
session = {:foo => :bar}
|
29
|
+
get '/', {}, 'rack.session' => session, 'HTTP_ACCEPT_LANGUAGE' => 'a'
|
30
|
+
get '/', {}, 'rack.session' => session, 'HTTP_ACCEPT_LANGUAGE' => 'b'
|
31
|
+
session.should be_empty
|
32
|
+
end
|
33
|
+
|
34
|
+
it "denies requests with a changing Version header"do
|
35
|
+
session = {:foo => :bar}
|
36
|
+
get '/', {}, 'rack.session' => session, 'HTTP_VERSION' => '1.0'
|
37
|
+
get '/', {}, 'rack.session' => session, 'HTTP_VERSION' => '1.1'
|
38
|
+
session.should be_empty
|
39
|
+
end
|
40
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'rack/protection'
|
2
|
+
require 'rack/test'
|
3
|
+
require 'forwardable'
|
4
|
+
require 'stringio'
|
5
|
+
|
6
|
+
if defined? Gem.loaded_specs and Gem.loaded_specs.include? 'rack'
|
7
|
+
version = Gem.loaded_specs['rack'].version.to_s
|
8
|
+
else
|
9
|
+
version = Rack.release + '.0'
|
10
|
+
end
|
11
|
+
|
12
|
+
if version == "1.3"
|
13
|
+
Rack::Session::Abstract::ID.class_eval do
|
14
|
+
private
|
15
|
+
def prepare_session(env)
|
16
|
+
session_was = env[ENV_SESSION_KEY]
|
17
|
+
env[ENV_SESSION_KEY] = SessionHash.new(self, env)
|
18
|
+
env[ENV_SESSION_OPTIONS_KEY] = OptionsHash.new(self, env, @default_options)
|
19
|
+
env[ENV_SESSION_KEY].merge! session_was if session_was
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module DummyApp
|
25
|
+
def self.call(env)
|
26
|
+
Thread.current[:last_env] = env
|
27
|
+
[200, {'Content-Type' => 'text/plain'}, ['ok']]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
module TestHelpers
|
32
|
+
extend Forwardable
|
33
|
+
def_delegators :last_response, :body, :headers, :status, :errors
|
34
|
+
def_delegators :current_session, :env_for
|
35
|
+
attr_writer :app
|
36
|
+
|
37
|
+
def app
|
38
|
+
@app || mock_app(DummyApp)
|
39
|
+
end
|
40
|
+
|
41
|
+
def mock_app(app = nil, &block)
|
42
|
+
app = block if app.nil? and block.arity == 1
|
43
|
+
if app
|
44
|
+
klass = described_class
|
45
|
+
mock_app do
|
46
|
+
use Rack::Head
|
47
|
+
use(Rack::Config) { |e| e['rack.session'] ||= {}}
|
48
|
+
use klass
|
49
|
+
run app
|
50
|
+
end
|
51
|
+
else
|
52
|
+
@app = Rack::Lint.new Rack::Builder.new(&block).to_app
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def with_headers(headers)
|
57
|
+
proc { [200, {'Content-Type' => 'text/plain'}.merge(headers), ['ok']] }
|
58
|
+
end
|
59
|
+
|
60
|
+
def env
|
61
|
+
Thread.current[:last_env]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# see http://blog.101ideas.cz/posts/pending-examples-via-not-implemented-error-in-rspec.html
|
66
|
+
module NotImplementedAsPending
|
67
|
+
def self.included(base)
|
68
|
+
base.class_eval do
|
69
|
+
alias_method :__finish__, :finish
|
70
|
+
remove_method :finish
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def finish(reporter)
|
75
|
+
if @exception.is_a?(NotImplementedError)
|
76
|
+
from = @exception.backtrace[0]
|
77
|
+
message = "#{@exception.message} (from #{from})"
|
78
|
+
@pending_declared_in_example = message
|
79
|
+
metadata[:pending] = true
|
80
|
+
@exception = nil
|
81
|
+
end
|
82
|
+
|
83
|
+
__finish__(reporter)
|
84
|
+
end
|
85
|
+
|
86
|
+
RSpec::Core::Example.send :include, self
|
87
|
+
end
|
88
|
+
|
89
|
+
RSpec.configure do |config|
|
90
|
+
config.expect_with :rspec, :stdlib
|
91
|
+
config.include Rack::Test::Methods
|
92
|
+
config.include TestHelpers
|
93
|
+
end
|
94
|
+
|
95
|
+
shared_examples_for 'any rack application' do
|
96
|
+
it "should not interfere with normal get requests" do
|
97
|
+
get('/').should be_ok
|
98
|
+
body.should == 'ok'
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should not interfere with normal head requests" do
|
102
|
+
head('/').should be_ok
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'should not leak changes to env' do
|
106
|
+
klass = described_class
|
107
|
+
detector = Struct.new(:app)
|
108
|
+
|
109
|
+
detector.send(:define_method, :call) do |env|
|
110
|
+
was = env.dup
|
111
|
+
res = app.call(env)
|
112
|
+
was.each do |k,v|
|
113
|
+
next if env[k] == v
|
114
|
+
fail "env[#{k.inspect}] changed from #{v.inspect} to #{env[k].inspect}"
|
115
|
+
end
|
116
|
+
res
|
117
|
+
end
|
118
|
+
|
119
|
+
mock_app do
|
120
|
+
use Rack::Head
|
121
|
+
use(Rack::Config) { |e| e['rack.session'] ||= {}}
|
122
|
+
use detector
|
123
|
+
use klass
|
124
|
+
run DummyApp
|
125
|
+
end
|
126
|
+
|
127
|
+
get('/..', :foo => '<bar>').should be_ok
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'allows passing on values in env' do
|
131
|
+
klass = described_class
|
132
|
+
detector = Struct.new(:app)
|
133
|
+
changer = Struct.new(:app)
|
134
|
+
|
135
|
+
detector.send(:define_method, :call) do |env|
|
136
|
+
res = app.call(env)
|
137
|
+
env['foo.bar'].should == 42
|
138
|
+
res
|
139
|
+
end
|
140
|
+
|
141
|
+
changer.send(:define_method, :call) do |env|
|
142
|
+
env['foo.bar'] = 42
|
143
|
+
app.call(env)
|
144
|
+
end
|
145
|
+
|
146
|
+
mock_app do
|
147
|
+
use Rack::Head
|
148
|
+
use(Rack::Config) { |e| e['rack.session'] ||= {}}
|
149
|
+
use detector
|
150
|
+
use klass
|
151
|
+
use changer
|
152
|
+
run DummyApp
|
153
|
+
end
|
154
|
+
|
155
|
+
get('/').should be_ok
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,24 @@
|
|
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('/').headers["X-XSS-Protection"].should == "1; mode=block"
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'should allow changing the protection mode' do
|
11
|
+
# I have no clue what other modes are available
|
12
|
+
mock_app do
|
13
|
+
use Rack::Protection::XSSHeader, :xss_mode => :foo
|
14
|
+
run DummyApp
|
15
|
+
end
|
16
|
+
|
17
|
+
get('/').headers["X-XSS-Protection"].should == "1; mode=foo"
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should not override the header if already set' do
|
21
|
+
mock_app with_headers("X-XSS-Protection" => "0")
|
22
|
+
get('/').headers["X-XSS-Protection"].should == "0"
|
23
|
+
end
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rack-protection
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Konstantin Haase
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-06-20 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rack
|
16
|
+
requirement: &2153646760 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *2153646760
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: escape_utils
|
27
|
+
requirement: &2153646220 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *2153646220
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rack-test
|
38
|
+
requirement: &2153645700 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *2153645700
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rspec
|
49
|
+
requirement: &2153645080 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *2153645080
|
58
|
+
description: You should use protection!
|
59
|
+
email:
|
60
|
+
- konstantin.mailinglists@googlemail.com
|
61
|
+
executables: []
|
62
|
+
extensions: []
|
63
|
+
extra_rdoc_files: []
|
64
|
+
files:
|
65
|
+
- License
|
66
|
+
- README.md
|
67
|
+
- Rakefile
|
68
|
+
- lib/rack-protection.rb
|
69
|
+
- lib/rack/protection.rb
|
70
|
+
- lib/rack/protection/authenticity_token.rb
|
71
|
+
- lib/rack/protection/base.rb
|
72
|
+
- lib/rack/protection/escaped_params.rb
|
73
|
+
- lib/rack/protection/form_token.rb
|
74
|
+
- lib/rack/protection/frame_options.rb
|
75
|
+
- lib/rack/protection/ip_spoofing.rb
|
76
|
+
- lib/rack/protection/json_csrf.rb
|
77
|
+
- lib/rack/protection/path_traversal.rb
|
78
|
+
- lib/rack/protection/remote_referrer.rb
|
79
|
+
- lib/rack/protection/remote_token.rb
|
80
|
+
- lib/rack/protection/session_hijacking.rb
|
81
|
+
- lib/rack/protection/version.rb
|
82
|
+
- lib/rack/protection/xss_header.rb
|
83
|
+
- rack-protection.gemspec
|
84
|
+
- spec/authenticity_token_spec.rb
|
85
|
+
- spec/escaped_params_spec.rb
|
86
|
+
- spec/form_token_spec.rb
|
87
|
+
- spec/frame_options_spec.rb
|
88
|
+
- spec/ip_spoofing_spec.rb
|
89
|
+
- spec/json_csrf_spec.rb
|
90
|
+
- spec/path_traversal_spec.rb
|
91
|
+
- spec/protection_spec.rb
|
92
|
+
- spec/remote_referrer_spec.rb
|
93
|
+
- spec/remote_token_spec.rb
|
94
|
+
- spec/session_hijacking_spec.rb
|
95
|
+
- spec/spec_helper.rb
|
96
|
+
- spec/xss_header_spec.rb
|
97
|
+
homepage: http://github.com/rkh/rack-protection
|
98
|
+
licenses: []
|
99
|
+
post_install_message:
|
100
|
+
rdoc_options: []
|
101
|
+
require_paths:
|
102
|
+
- lib
|
103
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
104
|
+
none: false
|
105
|
+
requirements:
|
106
|
+
- - ! '>='
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '0'
|
109
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
110
|
+
none: false
|
111
|
+
requirements:
|
112
|
+
- - ! '>='
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
requirements: []
|
116
|
+
rubyforge_project:
|
117
|
+
rubygems_version: 1.8.5
|
118
|
+
signing_key:
|
119
|
+
specification_version: 3
|
120
|
+
summary: You should use protection!
|
121
|
+
test_files: []
|