rack_csrf 2.5.0 → 2.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|