rack-protection 0.1.0
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.
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: []
|