rack_csrf 2.5.0 → 2.6.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.
- checksums.yaml +4 -4
- data/.gitignore +4 -1
- data/.travis.yml +18 -8
- data/Changelog.md +11 -0
- data/LICENSE.rdoc +1 -1
- data/README.rdoc +8 -1
- data/Rakefile +1 -1
- data/examples/camping/app.rb +1 -1
- data/examples/sinatra/app.rb +4 -2
- data/features/step_definitions/request_steps.rb +7 -7
- data/features/step_definitions/response_steps.rb +5 -5
- data/features/step_definitions/setup_steps.rb +11 -9
- data/features/support/fake_session.rb +2 -1
- data/lib/rack/csrf.rb +22 -17
- data/lib/rack/csrf/version.rb +1 -1
- data/rack_csrf.gemspec +3 -5
- data/spec/csrf_spec.rb +90 -43
- data/spec/spec_helper.rb +0 -7
- metadata +9 -23
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7481a1058bae8d7a4fef4806a5fb72599cc3b724
|
|
4
|
+
data.tar.gz: 0af8b6cec94f2ca0703cb72478199e30aa60fc17
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d5c1d649719acfc69b42e0a9760f9021e313cab550efff436d988ad4886646312ece6ae6ecd672546011848382924e402364d9454cbc844e5ea2bc2f6cb842c6
|
|
7
|
+
data.tar.gz: 25d4bec6bb2489c6999d0db1f9bd4969ebde37b9533b496c4ccde981c55949ee8922984feda32a56ec342d22de428c33a4be18204b8641d468b27594913d1a7c
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
|
@@ -1,15 +1,25 @@
|
|
|
1
|
+
sudo: false
|
|
2
|
+
|
|
1
3
|
language: ruby
|
|
2
4
|
|
|
3
5
|
rvm:
|
|
4
|
-
-
|
|
5
|
-
- 1.
|
|
6
|
-
-
|
|
7
|
-
- 2.
|
|
8
|
-
- 2.
|
|
6
|
+
- 2.0.0-p648
|
|
7
|
+
- 2.1.10
|
|
8
|
+
- 2.2.6
|
|
9
|
+
- 2.3.3
|
|
10
|
+
- 2.4.0
|
|
9
11
|
|
|
10
12
|
env:
|
|
11
|
-
- TEST_WITH_RACK=1.1.0
|
|
12
|
-
- TEST_WITH_RACK=1.2.0
|
|
13
|
-
- TEST_WITH_RACK=1.3.0
|
|
14
13
|
- TEST_WITH_RACK=1.4.0
|
|
15
14
|
- TEST_WITH_RACK=1.5.0
|
|
15
|
+
- TEST_WITH_RACK=1.6.0
|
|
16
|
+
- TEST_WITH_RACK=2.0.0
|
|
17
|
+
|
|
18
|
+
matrix:
|
|
19
|
+
exclude:
|
|
20
|
+
- rvm: 2.0.0-p648
|
|
21
|
+
env: TEST_WITH_RACK=2.0.0
|
|
22
|
+
- rvm: 2.1.10
|
|
23
|
+
env: TEST_WITH_RACK=2.0.0
|
|
24
|
+
|
|
25
|
+
script: bundle exec rake spec features
|
data/Changelog.md
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
# v2.6.0 (2016-12-31)
|
|
2
|
+
|
|
3
|
+
Many little, internal, changes; the important ones are:
|
|
4
|
+
|
|
5
|
+
* switched to use SecureRandom.urlsafe_base64 to make the token URL-friendly
|
|
6
|
+
(courtesy of [steved](https://github.com/steved));
|
|
7
|
+
* code is tested against Rack 1.4, 1.5, 1.6 and 2.0;
|
|
8
|
+
* code is tested only on Ruby 2.0.0 and later.
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
1
12
|
# v2.5.0 (2014-06-15)
|
|
2
13
|
|
|
3
14
|
* Fixed/improved the examples.
|
data/LICENSE.rdoc
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
(The MIT License)
|
|
4
4
|
|
|
5
|
-
Copyright (c) 2009, 2010, 2011, 2012, 2014 Emanuele Vicentini
|
|
5
|
+
Copyright (c) 2009, 2010, 2011, 2012, 2014, 2016 Emanuele Vicentini
|
|
6
6
|
|
|
7
7
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
8
|
of this software and associated documentation files (the 'Software'), to deal
|
data/README.rdoc
CHANGED
|
@@ -190,6 +190,13 @@ In the +examples+ directory there are some small, working web applications
|
|
|
190
190
|
written with different Rack-based frameworks. They are named after the used
|
|
191
191
|
framework; see the various README files for other details.
|
|
192
192
|
|
|
193
|
+
== Supported Rubies and Racks
|
|
194
|
+
|
|
195
|
+
The gemspec shows the minimum Ruby and Rack versions, but Rack::Csrf is
|
|
196
|
+
tested only with the Rubies and Racks you can see in +.travis.yml+. It could
|
|
197
|
+
work also with older versions, but I decided not to test it against
|
|
198
|
+
unsupported Rubies and Racks.
|
|
199
|
+
|
|
193
200
|
== Contributing
|
|
194
201
|
|
|
195
202
|
If you want to help:
|
|
@@ -212,5 +219,5 @@ forgo responsibilities for keeping your application as safe as possible.
|
|
|
212
219
|
|
|
213
220
|
== Copyright
|
|
214
221
|
|
|
215
|
-
Copyright (c) 2009, 2010, 2011, 2012, 2014 Emanuele Vicentini. See
|
|
222
|
+
Copyright (c) 2009, 2010, 2011, 2012, 2014, 2016 Emanuele Vicentini. See
|
|
216
223
|
LICENSE.rdoc for details.
|
data/Rakefile
CHANGED
|
@@ -31,7 +31,7 @@ Shows the changelog in Git between the given points.
|
|
|
31
31
|
start -- defaults to the current version tag
|
|
32
32
|
end -- defaults to HEAD
|
|
33
33
|
EOD
|
|
34
|
-
task :changes, [:start, :end] do |
|
|
34
|
+
task :changes, [:start, :end] do |_, args|
|
|
35
35
|
args.with_defaults :start => "v#{Rack::Csrf::VERSION}",
|
|
36
36
|
:end => 'HEAD'
|
|
37
37
|
repo = Git.open Dir.pwd
|
data/examples/camping/app.rb
CHANGED
|
@@ -6,7 +6,7 @@ Camping.goes :LittleApp
|
|
|
6
6
|
|
|
7
7
|
module LittleApp
|
|
8
8
|
use Rack::Csrf # This has to come BEFORE 'include Camping::Session',
|
|
9
|
-
# otherwise you get the 'Rack::Csrf depends on session
|
|
9
|
+
# otherwise you get the 'Rack::Csrf depends on session
|
|
10
10
|
# middleware' exception. Weird...
|
|
11
11
|
include Camping::Session
|
|
12
12
|
|
data/examples/sinatra/app.rb
CHANGED
|
@@ -7,6 +7,8 @@ get '/notworking' do
|
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
post '/response' do
|
|
10
|
-
erb :response, :locals => {
|
|
11
|
-
:
|
|
10
|
+
erb :response, :locals => {
|
|
11
|
+
:utterance => params[:utterance],
|
|
12
|
+
:csrf => params[Rack::Csrf.field]
|
|
13
|
+
}
|
|
12
14
|
end
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
When /^it receives a (.*) request without the CSRF (?:token|header)$/ do |http_method|
|
|
5
5
|
begin
|
|
6
6
|
@browser.request '/', :method => http_method
|
|
7
|
-
rescue
|
|
7
|
+
rescue StandardError => e
|
|
8
8
|
@exception = e
|
|
9
9
|
end
|
|
10
10
|
end
|
|
@@ -12,7 +12,7 @@ end
|
|
|
12
12
|
When /^it receives a (.*) request for (.+) without the CSRF (?:token|header|token or header)$/ do |http_method, path|
|
|
13
13
|
begin
|
|
14
14
|
@browser.request path, :method => http_method
|
|
15
|
-
rescue
|
|
15
|
+
rescue StandardError => e
|
|
16
16
|
@exception = e
|
|
17
17
|
end
|
|
18
18
|
end
|
|
@@ -34,7 +34,7 @@ When /^it receives a (.*) request with the wrong CSRF token$/ do |http_method|
|
|
|
34
34
|
@browser.request '/', :method => http_method,
|
|
35
35
|
'rack.session' => {Rack::Csrf.key => 'right_token'},
|
|
36
36
|
:params => {Rack::Csrf.field => 'wrong_token'}
|
|
37
|
-
rescue
|
|
37
|
+
rescue StandardError => e
|
|
38
38
|
@exception = e
|
|
39
39
|
end
|
|
40
40
|
end
|
|
@@ -43,7 +43,7 @@ When /^it receives a (.*) request with the wrong CSRF header/ do |http_method|
|
|
|
43
43
|
begin
|
|
44
44
|
@browser.request '/', :method => http_method,
|
|
45
45
|
Rack::Csrf.rackified_header => 'right_token'
|
|
46
|
-
rescue
|
|
46
|
+
rescue StandardError => e
|
|
47
47
|
@exception = e
|
|
48
48
|
end
|
|
49
49
|
end
|
|
@@ -51,7 +51,7 @@ end
|
|
|
51
51
|
When /^it receives a (.*) request with neither PATH_INFO nor CSRF token or header$/ do |http_method|
|
|
52
52
|
begin
|
|
53
53
|
@browser.request '/doesntmatter', :method => http_method, 'PATH_INFO' => ''
|
|
54
|
-
rescue
|
|
54
|
+
rescue StandardError => e
|
|
55
55
|
@exception = e
|
|
56
56
|
end
|
|
57
57
|
end
|
|
@@ -59,7 +59,7 @@ end
|
|
|
59
59
|
When /^it receives a request with headers (.+) = ([^ ]+) without the CSRF token or header$/ do |name, value|
|
|
60
60
|
begin
|
|
61
61
|
@browser.request '/', Hash[:method, 'POST', name, value]
|
|
62
|
-
rescue
|
|
62
|
+
rescue StandardError => e
|
|
63
63
|
@exception = e
|
|
64
64
|
end
|
|
65
65
|
end
|
|
@@ -67,7 +67,7 @@ end
|
|
|
67
67
|
When /^it receives a request with headers (.+) = ([^,]+), (.+), and without the CSRF token or header$/ do |name, value, method|
|
|
68
68
|
begin
|
|
69
69
|
@browser.request '/', Hash[:method, method, name, value]
|
|
70
|
-
rescue
|
|
70
|
+
rescue StandardError => e
|
|
71
71
|
@exception = e
|
|
72
72
|
end
|
|
73
73
|
end
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
Then /^it lets it pass untouched$/ do
|
|
2
|
-
@browser.last_response.
|
|
3
|
-
@browser.last_response.
|
|
2
|
+
expect(@browser.last_response).to be_ok
|
|
3
|
+
expect(@browser.last_response).to match(/Hello world!/)
|
|
4
4
|
end
|
|
5
5
|
|
|
6
6
|
Then /^it responds with (\d\d\d)$/ do |code|
|
|
7
|
-
@browser.last_response.status.
|
|
7
|
+
expect(@browser.last_response.status).to eq(code.to_i)
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
Then /^the response body is empty$/ do
|
|
11
|
-
@browser.last_response.body.
|
|
11
|
+
expect(@browser.last_response.body).to be_empty
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
Then /^there is no response$/ do
|
|
@@ -16,5 +16,5 @@ Then /^there is no response$/ do
|
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
Then /^an exception is climbing up the stack$/ do
|
|
19
|
-
@exception.
|
|
19
|
+
expect(@exception).to be_an_instance_of(Rack::Csrf::InvalidCsrfToken)
|
|
20
20
|
end
|
|
@@ -73,7 +73,7 @@ When /^I insert the anti\-CSRF middleware with the :raise option$/ do
|
|
|
73
73
|
end
|
|
74
74
|
|
|
75
75
|
When /^I insert the anti\-CSRF middleware with the :skip option$/ do |table|
|
|
76
|
-
skippable = table.hashes.collect
|
|
76
|
+
skippable = table.hashes.collect(&:values).flatten
|
|
77
77
|
@rack_builder.use Rack::Csrf, :skip => skippable
|
|
78
78
|
@app = toy_app
|
|
79
79
|
@browser = Rack::Test::Session.new(Rack::MockSession.new(@app))
|
|
@@ -82,9 +82,10 @@ end
|
|
|
82
82
|
When /^I insert the anti\-CSRF middleware with the :skip_if option$/ do |table|
|
|
83
83
|
skippable = {}
|
|
84
84
|
table.hashes.each {|row| skippable[row['name']] = row['value']}
|
|
85
|
-
|
|
85
|
+
skip_logic = Proc.new do |request|
|
|
86
86
|
skippable.any? { |name, value| request.env[name] == value }
|
|
87
|
-
|
|
87
|
+
end
|
|
88
|
+
@rack_builder.use Rack:: Csrf, :skip_if => skip_logic
|
|
88
89
|
@app = toy_app
|
|
89
90
|
@browser = Rack::Test::Session.new(Rack::MockSession.new(@app))
|
|
90
91
|
end
|
|
@@ -96,10 +97,11 @@ When /^I insert the anti\-CSRF middleware with the :skip and :skip_if options$/
|
|
|
96
97
|
skip_option_arguments << row['path']
|
|
97
98
|
skip_if_option_arguments[row['name']] = row['value']
|
|
98
99
|
end
|
|
99
|
-
|
|
100
|
-
@rack_builder.use Rack:: Csrf, :skip => skip_option_arguments, :skip_if => Proc.new { |request|
|
|
100
|
+
skip_if_logic = Proc.new do |request|
|
|
101
101
|
skip_if_option_arguments.any? { |name, value| request.env[name] == value }
|
|
102
|
-
|
|
102
|
+
end
|
|
103
|
+
@rack_builder.use Rack::Csrf, :skip => skip_option_arguments,
|
|
104
|
+
:skip_if => skip_if_logic
|
|
103
105
|
@app = toy_app
|
|
104
106
|
@browser = Rack::Test::Session.new(Rack::MockSession.new(@app))
|
|
105
107
|
end
|
|
@@ -123,14 +125,14 @@ When /^I insert the anti\-CSRF middleware with the :header option$/ do
|
|
|
123
125
|
end
|
|
124
126
|
|
|
125
127
|
When /^I insert the anti\-CSRF middleware with the :check_also option$/ do |table|
|
|
126
|
-
check_also = table.hashes.collect
|
|
128
|
+
check_also = table.hashes.collect(&:values).flatten
|
|
127
129
|
@rack_builder.use Rack::Csrf, :check_also => check_also
|
|
128
130
|
@app = toy_app
|
|
129
131
|
@browser = Rack::Test::Session.new(Rack::MockSession.new(@app))
|
|
130
132
|
end
|
|
131
133
|
|
|
132
134
|
When /^I insert the anti\-CSRF middleware with the :check_only option$/ do |table|
|
|
133
|
-
must_be_checked = table.hashes.collect
|
|
135
|
+
must_be_checked = table.hashes.collect(&:values).flatten
|
|
134
136
|
@rack_builder.use Rack::Csrf, :check_only => must_be_checked
|
|
135
137
|
@app = toy_app
|
|
136
138
|
@browser = Rack::Test::Session.new(Rack::MockSession.new(@app))
|
|
@@ -147,6 +149,6 @@ Then /^I get an error message$/ do
|
|
|
147
149
|
end
|
|
148
150
|
|
|
149
151
|
def toy_app
|
|
150
|
-
@rack_builder.run(lambda {|
|
|
152
|
+
@rack_builder.run(lambda {|_| Rack::Response.new('Hello world!').finish})
|
|
151
153
|
@rack_builder.to_app
|
|
152
154
|
end
|
data/lib/rack/csrf.rb
CHANGED
|
@@ -13,32 +13,31 @@ module Rack
|
|
|
13
13
|
def initialize(app, opts = {})
|
|
14
14
|
@app = app
|
|
15
15
|
|
|
16
|
-
@
|
|
17
|
-
@skip_list
|
|
18
|
-
@skip_if
|
|
19
|
-
@check_only_list
|
|
20
|
-
@@field
|
|
21
|
-
@@header
|
|
22
|
-
@@key
|
|
16
|
+
@raise_if_invalid = opts.fetch(:raise, false)
|
|
17
|
+
@skip_list = opts.fetch(:skip, []).map {|r| /\A#{r}\Z/i}
|
|
18
|
+
@skip_if = opts[:skip_if]
|
|
19
|
+
@check_only_list = opts.fetch(:check_only, []).map {|r| /\A#{r}\Z/i}
|
|
20
|
+
@@field = opts[:field] if opts[:field]
|
|
21
|
+
@@header = opts[:header] if opts[:header]
|
|
22
|
+
@@key = opts[:key] if opts[:key]
|
|
23
23
|
|
|
24
24
|
standard_http_methods = %w(POST PUT DELETE PATCH)
|
|
25
|
-
check_also = opts
|
|
25
|
+
check_also = opts.fetch(:check_also, [])
|
|
26
26
|
@http_methods = (standard_http_methods + check_also).flatten.uniq
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
def call(env)
|
|
30
30
|
unless env['rack.session']
|
|
31
|
-
|
|
31
|
+
fail SessionUnavailable, 'Rack::Csrf depends on session middleware'
|
|
32
32
|
end
|
|
33
33
|
req = Rack::Request.new(env)
|
|
34
|
-
|
|
34
|
+
let_it_pass = skip_checking(req) ||
|
|
35
35
|
!@http_methods.include?(req.request_method) ||
|
|
36
|
-
req
|
|
37
|
-
|
|
38
|
-
if untouchable
|
|
36
|
+
found_a_valid_token?(req)
|
|
37
|
+
if let_it_pass
|
|
39
38
|
@app.call(env)
|
|
40
39
|
else
|
|
41
|
-
|
|
40
|
+
fail InvalidCsrfToken if @raise_if_invalid
|
|
42
41
|
[403, {'Content-Type' => 'text/html', 'Content-Length' => '0'}, []]
|
|
43
42
|
end
|
|
44
43
|
end
|
|
@@ -56,7 +55,7 @@ module Rack
|
|
|
56
55
|
end
|
|
57
56
|
|
|
58
57
|
def self.token(env)
|
|
59
|
-
env['rack.session'][key] ||= SecureRandom.
|
|
58
|
+
env['rack.session'][key] ||= SecureRandom.urlsafe_base64(32)
|
|
60
59
|
end
|
|
61
60
|
|
|
62
61
|
def self.tag(env)
|
|
@@ -64,7 +63,7 @@ module Rack
|
|
|
64
63
|
end
|
|
65
64
|
|
|
66
65
|
def self.metatag(env, options = {})
|
|
67
|
-
name = options.
|
|
66
|
+
name = options.fetch(:name, '_csrf')
|
|
68
67
|
%Q(<meta name="#{name}" content="#{token(env)}" />)
|
|
69
68
|
end
|
|
70
69
|
|
|
@@ -81,7 +80,7 @@ module Rack
|
|
|
81
80
|
|
|
82
81
|
# Returns the custom header's name adapted to current standards.
|
|
83
82
|
def self.rackified_header
|
|
84
|
-
"HTTP_#{@@header.gsub('-','_').upcase}"
|
|
83
|
+
"HTTP_#{@@header.gsub('-', '_').upcase}"
|
|
85
84
|
end
|
|
86
85
|
|
|
87
86
|
# Returns +true+ if the given request appears in the <b>skip list</b> or
|
|
@@ -103,5 +102,11 @@ module Rack
|
|
|
103
102
|
route =~ (request.request_method + ':' + pi)
|
|
104
103
|
end
|
|
105
104
|
end
|
|
105
|
+
|
|
106
|
+
def found_a_valid_token? request
|
|
107
|
+
token = self.class.token(request.env)
|
|
108
|
+
Rack::Utils.secure_compare(request.params[self.class.field].to_s, token) ||
|
|
109
|
+
Rack::Utils.secure_compare(request.env[self.class.rackified_header].to_s, token)
|
|
110
|
+
end
|
|
106
111
|
end
|
|
107
112
|
end
|
data/lib/rack/csrf/version.rb
CHANGED
data/rack_csrf.gemspec
CHANGED
|
@@ -13,8 +13,7 @@ Gem::Specification.new do |spec|
|
|
|
13
13
|
spec.homepage = 'https://github.com/baldowl/rack_csrf'
|
|
14
14
|
spec.license = 'MIT'
|
|
15
15
|
|
|
16
|
-
spec.files = `git ls-files`.split(
|
|
17
|
-
spec.executables = spec.files.grep(%r{^bin/}) {|f| File.basename f}
|
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
|
18
17
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
19
18
|
spec.require_paths = ['lib']
|
|
20
19
|
|
|
@@ -31,7 +30,7 @@ Gem::Specification.new do |spec|
|
|
|
31
30
|
'README.rdoc'
|
|
32
31
|
]
|
|
33
32
|
|
|
34
|
-
spec.required_ruby_version = '>= 1.
|
|
33
|
+
spec.required_ruby_version = '>= 1.9.2'
|
|
35
34
|
|
|
36
35
|
if ENV['TEST_WITH_RACK']
|
|
37
36
|
spec.add_runtime_dependency 'rack', "~> #{ENV['TEST_WITH_RACK']}"
|
|
@@ -41,10 +40,9 @@ Gem::Specification.new do |spec|
|
|
|
41
40
|
|
|
42
41
|
spec.add_development_dependency 'bundler', '>= 1.0.0'
|
|
43
42
|
spec.add_development_dependency 'rake'
|
|
44
|
-
spec.add_development_dependency 'cucumber', '
|
|
43
|
+
spec.add_development_dependency 'cucumber', '~> 2.4'
|
|
45
44
|
spec.add_development_dependency 'rack-test', '>= 0'
|
|
46
45
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
|
47
|
-
spec.add_development_dependency 'rspec-collection_matchers'
|
|
48
46
|
spec.add_development_dependency 'rdoc', '>= 2.4.2'
|
|
49
47
|
spec.add_development_dependency 'git', '>= 1.2.5'
|
|
50
48
|
end
|
data/spec/csrf_spec.rb
CHANGED
|
@@ -3,58 +3,67 @@ require 'spec_helper'
|
|
|
3
3
|
describe Rack::Csrf do
|
|
4
4
|
describe 'key' do
|
|
5
5
|
it "should be 'csrf.token' by default" do
|
|
6
|
-
Rack::Csrf.key.
|
|
6
|
+
expect(Rack::Csrf.key).to eq('csrf.token')
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
-
it
|
|
9
|
+
it 'should be the value of the :key option' do
|
|
10
10
|
Rack::Csrf.new nil, :key => 'whatever'
|
|
11
|
-
Rack::Csrf.key.
|
|
11
|
+
expect(Rack::Csrf.key).to eq('whatever')
|
|
12
12
|
end
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
describe 'csrf_key' do
|
|
16
16
|
it 'should be the same as method key' do
|
|
17
|
-
Rack::Csrf.method(:csrf_key).
|
|
17
|
+
expect(Rack::Csrf.method(:csrf_key)).to eq(Rack::Csrf.method(:key))
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
describe 'field' do
|
|
22
22
|
it "should be '_csrf' by default" do
|
|
23
|
-
Rack::Csrf.field.
|
|
23
|
+
expect(Rack::Csrf.field).to eq('_csrf')
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
it
|
|
26
|
+
it 'should be the value of :field option' do
|
|
27
27
|
Rack::Csrf.new nil, :field => 'whatever'
|
|
28
|
-
Rack::Csrf.field.
|
|
28
|
+
expect(Rack::Csrf.field).to eq('whatever')
|
|
29
29
|
end
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
describe 'csrf_field' do
|
|
33
33
|
it 'should be the same as method field' do
|
|
34
|
-
Rack::Csrf.method(:csrf_field).
|
|
34
|
+
expect(Rack::Csrf.method(:csrf_field)).to eq(Rack::Csrf.method(:field))
|
|
35
35
|
end
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
describe 'header' do
|
|
39
39
|
subject { Rack::Csrf.header }
|
|
40
|
-
it {
|
|
40
|
+
it { is_expected.to be == 'X_CSRF_TOKEN' }
|
|
41
41
|
|
|
42
|
-
context
|
|
42
|
+
context 'when set to something' do
|
|
43
43
|
before { Rack::Csrf.new nil, :header => 'something' }
|
|
44
44
|
subject { Rack::Csrf.header }
|
|
45
|
-
it {
|
|
45
|
+
it { is_expected.to be == 'something' }
|
|
46
46
|
end
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
describe 'csrf_header' do
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
it 'should be the same as method header' do
|
|
51
|
+
expect(Rack::Csrf.method(:csrf_header)).to eq(Rack::Csrf.method(:header))
|
|
52
|
+
end
|
|
52
53
|
end
|
|
53
54
|
|
|
54
55
|
describe 'token(env)' do
|
|
55
56
|
let(:env) { {'rack.session' => {}} }
|
|
56
57
|
|
|
57
|
-
|
|
58
|
+
context 'should produce a token' do
|
|
59
|
+
specify 'with at least 32 characters' do
|
|
60
|
+
expect(Rack::Csrf.token(env).length).to be >= 32
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
specify 'without +, / or =' do
|
|
64
|
+
expect(Rack::Csrf.token(env)).not_to match(/\+|\/|=/)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
58
67
|
|
|
59
68
|
context 'when accessing/manipulating the session' do
|
|
60
69
|
before do
|
|
@@ -62,17 +71,17 @@ describe Rack::Csrf do
|
|
|
62
71
|
end
|
|
63
72
|
|
|
64
73
|
it 'should use the key provided by method key' do
|
|
65
|
-
env['rack.session'].
|
|
74
|
+
expect(env['rack.session']).to be_empty
|
|
66
75
|
Rack::Csrf.token env
|
|
67
|
-
env['rack.session'][Rack::Csrf.key].
|
|
76
|
+
expect(env['rack.session'][Rack::Csrf.key]).not_to be_nil
|
|
68
77
|
end
|
|
69
78
|
end
|
|
70
79
|
|
|
71
80
|
context 'when the session does not already contain the token' do
|
|
72
81
|
it 'should store the token inside the session' do
|
|
73
|
-
env['rack.session'].
|
|
82
|
+
expect(env['rack.session']).to be_empty
|
|
74
83
|
token = Rack::Csrf.token(env)
|
|
75
|
-
token.
|
|
84
|
+
expect(token).to eq(env['rack.session'][Rack::Csrf.key])
|
|
76
85
|
end
|
|
77
86
|
end
|
|
78
87
|
|
|
@@ -82,14 +91,14 @@ describe Rack::Csrf do
|
|
|
82
91
|
end
|
|
83
92
|
|
|
84
93
|
it 'should get the token from the session' do
|
|
85
|
-
env['rack.session'][Rack::Csrf.key].
|
|
94
|
+
expect(env['rack.session'][Rack::Csrf.key]).to eq(Rack::Csrf.token(env))
|
|
86
95
|
end
|
|
87
96
|
end
|
|
88
97
|
end
|
|
89
98
|
|
|
90
99
|
describe 'csrf_token(env)' do
|
|
91
100
|
it 'should be the same as method token(env)' do
|
|
92
|
-
Rack::Csrf.method(:csrf_token).
|
|
101
|
+
expect(Rack::Csrf.method(:csrf_token)).to eq(Rack::Csrf.method(:token))
|
|
93
102
|
end
|
|
94
103
|
end
|
|
95
104
|
|
|
@@ -102,26 +111,26 @@ describe Rack::Csrf do
|
|
|
102
111
|
end
|
|
103
112
|
|
|
104
113
|
it 'should be an input field' do
|
|
105
|
-
tag.
|
|
114
|
+
expect(tag).to match(/^<input/)
|
|
106
115
|
end
|
|
107
116
|
|
|
108
117
|
it 'should be an hidden input field' do
|
|
109
|
-
tag.
|
|
118
|
+
expect(tag).to match(/type="hidden"/)
|
|
110
119
|
end
|
|
111
120
|
|
|
112
|
-
it
|
|
113
|
-
tag.
|
|
121
|
+
it 'should have the name provided by method field' do
|
|
122
|
+
expect(tag).to match(/name="#{Rack::Csrf.field}"/)
|
|
114
123
|
end
|
|
115
124
|
|
|
116
|
-
it
|
|
125
|
+
it 'should have the value provided by method token(env)' do
|
|
117
126
|
quoted_value = Regexp.quote %Q(value="#{Rack::Csrf.token(env)}")
|
|
118
|
-
tag.
|
|
127
|
+
expect(tag).to match(/#{quoted_value}/)
|
|
119
128
|
end
|
|
120
129
|
end
|
|
121
130
|
|
|
122
131
|
describe 'csrf_tag(env)' do
|
|
123
132
|
it 'should be the same as method tag(env)' do
|
|
124
|
-
Rack::Csrf.method(:csrf_tag).
|
|
133
|
+
expect(Rack::Csrf.method(:csrf_tag)).to eq(Rack::Csrf.method(:tag))
|
|
125
134
|
end
|
|
126
135
|
end
|
|
127
136
|
|
|
@@ -135,11 +144,11 @@ describe Rack::Csrf do
|
|
|
135
144
|
end
|
|
136
145
|
|
|
137
146
|
subject { metatag }
|
|
138
|
-
it {
|
|
139
|
-
it {
|
|
140
|
-
it
|
|
147
|
+
it { is_expected.to match(/^<meta/) }
|
|
148
|
+
it { is_expected.to match(/name="_csrf"/) }
|
|
149
|
+
it 'should have the content provided by method token(env)' do
|
|
141
150
|
quoted_value = Regexp.quote %Q(content="#{Rack::Csrf.token(env)}")
|
|
142
|
-
metatag.
|
|
151
|
+
expect(metatag).to match(/#{quoted_value}/)
|
|
143
152
|
end
|
|
144
153
|
end
|
|
145
154
|
|
|
@@ -150,18 +159,18 @@ describe Rack::Csrf do
|
|
|
150
159
|
end
|
|
151
160
|
|
|
152
161
|
subject { metatag }
|
|
153
|
-
it {
|
|
154
|
-
it {
|
|
155
|
-
it
|
|
162
|
+
it { is_expected.to match(/^<meta/) }
|
|
163
|
+
it { is_expected.to match(/name="custom_name"/) }
|
|
164
|
+
it 'should have the content provided by method token(env)' do
|
|
156
165
|
quoted_value = Regexp.quote %Q(content="#{Rack::Csrf.token(env)}")
|
|
157
|
-
metatag.
|
|
166
|
+
expect(metatag).to match(/#{quoted_value}/)
|
|
158
167
|
end
|
|
159
168
|
end
|
|
160
169
|
end
|
|
161
170
|
|
|
162
171
|
describe 'csrf_metatag(env)' do
|
|
163
172
|
it 'should be the same as method metatag(env)' do
|
|
164
|
-
Rack::Csrf.method(:csrf_metatag).
|
|
173
|
+
expect(Rack::Csrf.method(:csrf_metatag)).to eq(Rack::Csrf.method(:metatag))
|
|
165
174
|
end
|
|
166
175
|
end
|
|
167
176
|
|
|
@@ -170,7 +179,7 @@ describe Rack::Csrf do
|
|
|
170
179
|
describe 'rackified_header' do
|
|
171
180
|
before { Rack::Csrf.new nil, :header => 'my-header' }
|
|
172
181
|
subject { Rack::Csrf.rackified_header }
|
|
173
|
-
it {
|
|
182
|
+
it { is_expected.to be == 'HTTP_MY_HEADER'}
|
|
174
183
|
end
|
|
175
184
|
|
|
176
185
|
describe 'skip_checking' do
|
|
@@ -185,7 +194,7 @@ describe Rack::Csrf do
|
|
|
185
194
|
let(:csrf) { Rack::Csrf.new nil }
|
|
186
195
|
|
|
187
196
|
it 'should run the check' do
|
|
188
|
-
csrf.send(:skip_checking, request).
|
|
197
|
+
expect(csrf.send(:skip_checking, request)).to be false
|
|
189
198
|
end
|
|
190
199
|
end
|
|
191
200
|
|
|
@@ -193,7 +202,7 @@ describe Rack::Csrf do
|
|
|
193
202
|
let(:csrf) { Rack::Csrf.new nil, :skip => ['POST:/hello'] }
|
|
194
203
|
|
|
195
204
|
it 'should not run the check' do
|
|
196
|
-
csrf.send(:skip_checking, request).
|
|
205
|
+
expect(csrf.send(:skip_checking, request)).to be true
|
|
197
206
|
end
|
|
198
207
|
end
|
|
199
208
|
|
|
@@ -202,7 +211,7 @@ describe Rack::Csrf do
|
|
|
202
211
|
let(:csrf) { Rack::Csrf.new nil, :skip_if => lambda { |req| req.env.key?('HTTP_X_VERY_SPECIAL_HEADER') } }
|
|
203
212
|
|
|
204
213
|
it 'should not run the check' do
|
|
205
|
-
csrf.send(:skip_checking, request).
|
|
214
|
+
expect(csrf.send(:skip_checking, request)).to be true
|
|
206
215
|
end
|
|
207
216
|
end
|
|
208
217
|
|
|
@@ -211,7 +220,7 @@ describe Rack::Csrf do
|
|
|
211
220
|
let(:csrf) { Rack::Csrf.new nil, :check_only => [] }
|
|
212
221
|
|
|
213
222
|
it 'should run the check' do
|
|
214
|
-
csrf.send(:skip_checking, request).
|
|
223
|
+
expect(csrf.send(:skip_checking, request)).to be false
|
|
215
224
|
end
|
|
216
225
|
end
|
|
217
226
|
|
|
@@ -220,7 +229,7 @@ describe Rack::Csrf do
|
|
|
220
229
|
let(:csrf) { Rack::Csrf.new nil, :check_only => ['POST:/hello'] }
|
|
221
230
|
|
|
222
231
|
it 'should run the check' do
|
|
223
|
-
csrf.send(:skip_checking, request).
|
|
232
|
+
expect(csrf.send(:skip_checking, request)).to be false
|
|
224
233
|
end
|
|
225
234
|
end
|
|
226
235
|
|
|
@@ -228,11 +237,49 @@ describe Rack::Csrf do
|
|
|
228
237
|
let(:csrf) { Rack::Csrf.new nil, :check_only => ['POST:/ciao'] }
|
|
229
238
|
|
|
230
239
|
it 'should not run the check' do
|
|
231
|
-
csrf.send(:skip_checking, request).
|
|
240
|
+
expect(csrf.send(:skip_checking, request)).to be true
|
|
232
241
|
end
|
|
233
242
|
end
|
|
234
243
|
end
|
|
235
244
|
end
|
|
236
245
|
end
|
|
237
246
|
end
|
|
247
|
+
|
|
248
|
+
describe 'found_a_valid_token?' do
|
|
249
|
+
let(:env) { {'rack.session' => {}} }
|
|
250
|
+
|
|
251
|
+
let(:csrf) { Rack::Csrf.new nil }
|
|
252
|
+
|
|
253
|
+
let(:mock_request_env) do
|
|
254
|
+
Rack::MockRequest.env_for '/hello',
|
|
255
|
+
:method => 'POST',
|
|
256
|
+
:input => 'foo=bar'
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
before do
|
|
260
|
+
Rack::Csrf.token env
|
|
261
|
+
mock_request_env['rack.session'] = env['rack.session']
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
context 'should be true' do
|
|
265
|
+
specify "if a valid token can be found in the request's paramaters" do
|
|
266
|
+
mock_request_env['rack.input'] = StringIO.new("#{Rack::Csrf.field}=#{Rack::Csrf.token(env)}")
|
|
267
|
+
request = Rack::Request.new(mock_request_env)
|
|
268
|
+
expect(csrf.send(:found_a_valid_token?, request)).to be true
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
specify "if a valid token can be found in the request's headers" do
|
|
272
|
+
mock_request_env[Rack::Csrf.rackified_header] = Rack::Csrf.token(env)
|
|
273
|
+
request = Rack::Request.new(mock_request_env)
|
|
274
|
+
expect(csrf.send(:found_a_valid_token?, request)).to be true
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
context 'should be false' do
|
|
279
|
+
specify 'if no valid token can be found anywhere' do
|
|
280
|
+
request = Rack::Request.new(mock_request_env)
|
|
281
|
+
expect(csrf.send(:found_a_valid_token?, request)).to be false
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
end
|
|
238
285
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rack_csrf
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Emanuele Vicentini
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2016-12-31 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rack
|
|
@@ -56,16 +56,16 @@ dependencies:
|
|
|
56
56
|
name: cucumber
|
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
|
58
58
|
requirements:
|
|
59
|
-
- - "
|
|
59
|
+
- - "~>"
|
|
60
60
|
- !ruby/object:Gem::Version
|
|
61
|
-
version:
|
|
61
|
+
version: '2.4'
|
|
62
62
|
type: :development
|
|
63
63
|
prerelease: false
|
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
65
|
requirements:
|
|
66
|
-
- - "
|
|
66
|
+
- - "~>"
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
|
-
version:
|
|
68
|
+
version: '2.4'
|
|
69
69
|
- !ruby/object:Gem::Dependency
|
|
70
70
|
name: rack-test
|
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -94,20 +94,6 @@ dependencies:
|
|
|
94
94
|
- - "~>"
|
|
95
95
|
- !ruby/object:Gem::Version
|
|
96
96
|
version: '3.0'
|
|
97
|
-
- !ruby/object:Gem::Dependency
|
|
98
|
-
name: rspec-collection_matchers
|
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
|
100
|
-
requirements:
|
|
101
|
-
- - ">="
|
|
102
|
-
- !ruby/object:Gem::Version
|
|
103
|
-
version: '0'
|
|
104
|
-
type: :development
|
|
105
|
-
prerelease: false
|
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
-
requirements:
|
|
108
|
-
- - ">="
|
|
109
|
-
- !ruby/object:Gem::Version
|
|
110
|
-
version: '0'
|
|
111
97
|
- !ruby/object:Gem::Dependency
|
|
112
98
|
name: rdoc
|
|
113
99
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -217,7 +203,7 @@ rdoc_options:
|
|
|
217
203
|
- "--line-numbers"
|
|
218
204
|
- "--inline-source"
|
|
219
205
|
- "--title"
|
|
220
|
-
- Rack::Csrf 2.
|
|
206
|
+
- Rack::Csrf 2.6.0
|
|
221
207
|
- "--main"
|
|
222
208
|
- README.rdoc
|
|
223
209
|
require_paths:
|
|
@@ -226,7 +212,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
226
212
|
requirements:
|
|
227
213
|
- - ">="
|
|
228
214
|
- !ruby/object:Gem::Version
|
|
229
|
-
version: 1.
|
|
215
|
+
version: 1.9.2
|
|
230
216
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
231
217
|
requirements:
|
|
232
218
|
- - ">="
|
|
@@ -234,7 +220,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
234
220
|
version: '0'
|
|
235
221
|
requirements: []
|
|
236
222
|
rubyforge_project:
|
|
237
|
-
rubygems_version: 2.
|
|
223
|
+
rubygems_version: 2.6.8
|
|
238
224
|
signing_key:
|
|
239
225
|
specification_version: 4
|
|
240
226
|
summary: Anti-CSRF Rack middleware
|