unified_csrf_prevention 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/.rspec +1 -0
  4. data/.rubocop.yml +24 -0
  5. data/Appraisals +17 -0
  6. data/CHANGELOG.md +6 -0
  7. data/Gemfile +5 -0
  8. data/MIT-LICENSE +21 -0
  9. data/README.md +90 -0
  10. data/Rakefile +3 -0
  11. data/lib/unified_csrf_prevention/core.rb +48 -0
  12. data/lib/unified_csrf_prevention/middleware.rb +48 -0
  13. data/lib/unified_csrf_prevention/railtie.rb +22 -0
  14. data/lib/unified_csrf_prevention/request_forgery_protection.rb +68 -0
  15. data/lib/unified_csrf_prevention/version.rb +5 -0
  16. data/lib/unified_csrf_prevention.rb +8 -0
  17. data/spec/controllers/dummy_controller_spec.rb +211 -0
  18. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  19. data/spec/dummy/app/controllers/dummy_controller.rb +11 -0
  20. data/spec/dummy/app/views/dummy/index.html.erb +1 -0
  21. data/spec/dummy/app/views/layouts/application.html.erb +12 -0
  22. data/spec/dummy/config/application.rb +32 -0
  23. data/spec/dummy/config/boot.rb +3 -0
  24. data/spec/dummy/config/environment.rb +5 -0
  25. data/spec/dummy/config/environments/test.rb +44 -0
  26. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  27. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  28. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  29. data/spec/dummy/config/initializers/inflections.rb +16 -0
  30. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  31. data/spec/dummy/config/initializers/session_store.rb +3 -0
  32. data/spec/dummy/config/initializers/to_time_preserves_timezone.rb +10 -0
  33. data/spec/dummy/config/initializers/wrap_parameters.rb +9 -0
  34. data/spec/dummy/config/routes.rb +5 -0
  35. data/spec/dummy/config/secrets.yml +22 -0
  36. data/spec/dummy/config.ru +4 -0
  37. data/spec/dummy/log/.keep +0 -0
  38. data/spec/rails_helper.rb +9 -0
  39. data/spec/spec_helper.rb +22 -0
  40. data/spec/support/shared_context/for_controllers.rb +53 -0
  41. data/spec/support/shared_examples/for_controllers.rb +77 -0
  42. data/spec/support/shared_examples/for_middleware.rb +35 -0
  43. data/spec/xing/csrf_protection/core_spec.rb +100 -0
  44. data/spec/xing/csrf_protection/middleware_spec.rb +44 -0
  45. data/unified_csrf_prevention.gemspec +34 -0
  46. metadata +215 -0
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ describe UnifiedCsrfPrevention::Core do
6
+ describe '#generate_token' do
7
+ subject { described_class.generate_token }
8
+
9
+ it { is_expected.to match(/\A[\w\-]+\z/) }
10
+ it { expect(subject.length).to eq ActionController::RequestForgeryProtection::AUTHENTICITY_TOKEN_LENGTH }
11
+
12
+ context 'randomness source' do
13
+ let(:bytes) { double }
14
+ let(:encoded_bytes) { double }
15
+ let(:encoded_bytes_substring) { double }
16
+
17
+ before do
18
+ allow(SecureRandom).to receive(:random_bytes).and_return bytes
19
+ allow(Base64).to receive(:urlsafe_encode64).with(bytes, anything).and_return encoded_bytes
20
+ allow(encoded_bytes).to receive(:[]).and_return(encoded_bytes_substring)
21
+ end
22
+
23
+ it { is_expected.to be encoded_bytes_substring }
24
+ end
25
+ end
26
+
27
+ describe '#checksum_for' do
28
+ let(:token) { 'a token' }
29
+
30
+ subject { described_class.checksum_for(token) }
31
+
32
+ context 'when the configuration variable is not set' do
33
+ before do
34
+ allow(Rails.configuration).to receive(:unified_csrf_prevention_key).and_raise(NoMethodError)
35
+ end
36
+
37
+ it { expect { subject }.to raise_error UnifiedCsrfPrevention::ConfigurationError }
38
+ end
39
+
40
+ context 'when the configuration variable is set' do
41
+ let(:shared_secret_key) { 'a shared secret key' }
42
+ before do
43
+ allow(Rails.configuration).to receive(:unified_csrf_prevention_key).and_return(shared_secret_key)
44
+ end
45
+
46
+ it { is_expected.to match(/\A[\w\-]+\z/) }
47
+
48
+ context 'example from the specification' do
49
+ let(:token) { 'such protect' }
50
+ let(:shared_secret_key) { 'much secure' }
51
+
52
+ it { is_expected.to eq 'fEFyEXot47K5knjFe7MB-CKW4q99a7BmP9rKwrxf9Qk' }
53
+ end
54
+ end
55
+ end
56
+
57
+ describe '#valid_token?' do
58
+ let(:token) { double }
59
+ let(:checksum) { double }
60
+
61
+ subject { described_class.valid_token?(token, checksum) }
62
+
63
+ context 'token is missing' do
64
+ let(:token) {}
65
+
66
+ it { is_expected.to be false }
67
+ end
68
+
69
+ context 'checksum is missing' do
70
+ let(:checksum) {}
71
+
72
+ it { is_expected.to be false }
73
+ end
74
+
75
+ context 'comparing checksums' do
76
+ let(:computed_checksum) { double }
77
+
78
+ before do
79
+ allow(described_class).to receive(:checksum_for).with(token).and_return(computed_checksum)
80
+ allow(ActiveSupport::SecurityUtils).to receive(:secure_compare).and_return(checksums_equal?)
81
+ end
82
+
83
+ after do
84
+ expect(ActiveSupport::SecurityUtils).to have_received(:secure_compare).with(computed_checksum, checksum)
85
+ end
86
+
87
+ context 'invalid checksum' do
88
+ let(:checksums_equal?) { false }
89
+
90
+ it { is_expected.to be false }
91
+ end
92
+
93
+ context 'valid checksum' do
94
+ let(:checksums_equal?) { true }
95
+
96
+ it { is_expected.to be true }
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rack'
4
+
5
+ describe UnifiedCsrfPrevention::Middleware do
6
+ let(:app) do
7
+ proc do |env|
8
+ env['unified_csrf_prevention.token'] = token unless token.nil?
9
+ [200, {}, 'some body']
10
+ end
11
+ end
12
+ let(:middleware) { described_class.new(app) }
13
+ let(:request) { Rack::MockRequest.new(middleware) }
14
+
15
+ subject { request.get('/what-ever') }
16
+
17
+ context 'when the application did not set the token' do
18
+ let(:token) {}
19
+
20
+ it_behaves_like 'a middleware that does not set any cookie'
21
+ end
22
+
23
+ context 'when the application set the token' do
24
+ let(:token) { 'token' }
25
+ let(:checksum) { 'some_checksum' }
26
+
27
+ before do
28
+ allow(UnifiedCsrfPrevention::Core).to receive(:checksum_for).with(token).and_return(checksum)
29
+ end
30
+
31
+ %w[production preview].each do |environment|
32
+ context "in #{environment} environment" do
33
+ before { allow(Rails.env).to receive(:"#{environment}?").and_return(true) }
34
+
35
+ it_behaves_like 'a middleware that sets secure csrf cookies'
36
+ it_behaves_like 'a middleware that logs generated csrf token'
37
+ end
38
+ end
39
+
40
+ context 'in some other environment' do
41
+ it_behaves_like 'a middleware that sets insecure csrf cookies'
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'English'
4
+
5
+ lib = File.expand_path('../lib', __FILE__)
6
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
7
+ require 'unified_csrf_prevention/version'
8
+
9
+ Gem::Specification.new do |s|
10
+ s.name = 'unified_csrf_prevention'
11
+ s.version = UnifiedCsrfPrevention::VERSION
12
+ s.authors = ['Egor Balyshev']
13
+ s.email = ['egor.balyshev@gmail.com']
14
+ s.summary = 'Cross-application CSRF prevention for Rails'
15
+ s.description = 'Unified stateless cross-application CSRF prevention implementation for Rails'
16
+ s.homepage = 'https://github.com/xing/unified_csrf_prevention'
17
+ s.license = 'MIT'
18
+
19
+ s.required_rubygems_version = '>= 2.0'
20
+
21
+ s.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
22
+ s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
23
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
24
+ s.require_paths = ['lib']
25
+
26
+ s.add_runtime_dependency 'rails', '>= 4.2'
27
+
28
+ s.add_development_dependency 'appraisal'
29
+ s.add_development_dependency 'bundler', '~> 1.12'
30
+ s.add_development_dependency 'rubocop', '~> 0.49.1'
31
+ s.add_development_dependency 'rspec', '~> 3.6'
32
+ s.add_development_dependency 'rspec-rails', '~> 3.6'
33
+ s.add_development_dependency 'rake', '~> 12.0'
34
+ end
metadata ADDED
@@ -0,0 +1,215 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: unified_csrf_prevention
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Egor Balyshev
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-07-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '4.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '4.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: appraisal
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.12'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.12'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.49.1
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.49.1
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.6'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.6'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec-rails
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.6'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.6'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rake
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '12.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '12.0'
111
+ description: Unified stateless cross-application CSRF prevention implementation for
112
+ Rails
113
+ email:
114
+ - egor.balyshev@gmail.com
115
+ executables: []
116
+ extensions: []
117
+ extra_rdoc_files: []
118
+ files:
119
+ - ".gitignore"
120
+ - ".rspec"
121
+ - ".rubocop.yml"
122
+ - Appraisals
123
+ - CHANGELOG.md
124
+ - Gemfile
125
+ - MIT-LICENSE
126
+ - README.md
127
+ - Rakefile
128
+ - lib/unified_csrf_prevention.rb
129
+ - lib/unified_csrf_prevention/core.rb
130
+ - lib/unified_csrf_prevention/middleware.rb
131
+ - lib/unified_csrf_prevention/railtie.rb
132
+ - lib/unified_csrf_prevention/request_forgery_protection.rb
133
+ - lib/unified_csrf_prevention/version.rb
134
+ - spec/controllers/dummy_controller_spec.rb
135
+ - spec/dummy/app/controllers/application_controller.rb
136
+ - spec/dummy/app/controllers/dummy_controller.rb
137
+ - spec/dummy/app/views/dummy/index.html.erb
138
+ - spec/dummy/app/views/layouts/application.html.erb
139
+ - spec/dummy/config.ru
140
+ - spec/dummy/config/application.rb
141
+ - spec/dummy/config/boot.rb
142
+ - spec/dummy/config/environment.rb
143
+ - spec/dummy/config/environments/test.rb
144
+ - spec/dummy/config/initializers/backtrace_silencers.rb
145
+ - spec/dummy/config/initializers/cookies_serializer.rb
146
+ - spec/dummy/config/initializers/filter_parameter_logging.rb
147
+ - spec/dummy/config/initializers/inflections.rb
148
+ - spec/dummy/config/initializers/mime_types.rb
149
+ - spec/dummy/config/initializers/session_store.rb
150
+ - spec/dummy/config/initializers/to_time_preserves_timezone.rb
151
+ - spec/dummy/config/initializers/wrap_parameters.rb
152
+ - spec/dummy/config/routes.rb
153
+ - spec/dummy/config/secrets.yml
154
+ - spec/dummy/log/.keep
155
+ - spec/rails_helper.rb
156
+ - spec/spec_helper.rb
157
+ - spec/support/shared_context/for_controllers.rb
158
+ - spec/support/shared_examples/for_controllers.rb
159
+ - spec/support/shared_examples/for_middleware.rb
160
+ - spec/xing/csrf_protection/core_spec.rb
161
+ - spec/xing/csrf_protection/middleware_spec.rb
162
+ - unified_csrf_prevention.gemspec
163
+ homepage: https://github.com/xing/unified_csrf_prevention
164
+ licenses:
165
+ - MIT
166
+ metadata: {}
167
+ post_install_message:
168
+ rdoc_options: []
169
+ require_paths:
170
+ - lib
171
+ required_ruby_version: !ruby/object:Gem::Requirement
172
+ requirements:
173
+ - - ">="
174
+ - !ruby/object:Gem::Version
175
+ version: '0'
176
+ required_rubygems_version: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '2.0'
181
+ requirements: []
182
+ rubyforge_project:
183
+ rubygems_version: 2.6.13
184
+ signing_key:
185
+ specification_version: 4
186
+ summary: Cross-application CSRF prevention for Rails
187
+ test_files:
188
+ - spec/controllers/dummy_controller_spec.rb
189
+ - spec/dummy/app/controllers/application_controller.rb
190
+ - spec/dummy/app/controllers/dummy_controller.rb
191
+ - spec/dummy/app/views/dummy/index.html.erb
192
+ - spec/dummy/app/views/layouts/application.html.erb
193
+ - spec/dummy/config.ru
194
+ - spec/dummy/config/application.rb
195
+ - spec/dummy/config/boot.rb
196
+ - spec/dummy/config/environment.rb
197
+ - spec/dummy/config/environments/test.rb
198
+ - spec/dummy/config/initializers/backtrace_silencers.rb
199
+ - spec/dummy/config/initializers/cookies_serializer.rb
200
+ - spec/dummy/config/initializers/filter_parameter_logging.rb
201
+ - spec/dummy/config/initializers/inflections.rb
202
+ - spec/dummy/config/initializers/mime_types.rb
203
+ - spec/dummy/config/initializers/session_store.rb
204
+ - spec/dummy/config/initializers/to_time_preserves_timezone.rb
205
+ - spec/dummy/config/initializers/wrap_parameters.rb
206
+ - spec/dummy/config/routes.rb
207
+ - spec/dummy/config/secrets.yml
208
+ - spec/dummy/log/.keep
209
+ - spec/rails_helper.rb
210
+ - spec/spec_helper.rb
211
+ - spec/support/shared_context/for_controllers.rb
212
+ - spec/support/shared_examples/for_controllers.rb
213
+ - spec/support/shared_examples/for_middleware.rb
214
+ - spec/xing/csrf_protection/core_spec.rb
215
+ - spec/xing/csrf_protection/middleware_spec.rb