angular_rails_csrf 4.0.0 → 4.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +34 -3
- data/Rakefile +3 -6
- data/lib/angular_rails_csrf.rb +2 -0
- data/lib/angular_rails_csrf/concern.rb +38 -12
- data/lib/angular_rails_csrf/railtie.rb +2 -0
- data/lib/angular_rails_csrf/version.rb +3 -1
- data/test/angular_rails_csrf_exception_test.rb +4 -2
- data/test/angular_rails_csrf_skip_test.rb +14 -0
- data/test/angular_rails_csrf_test.rb +85 -19
- data/test/dummy/app/assets/config/manifest.js +4 -0
- data/test/dummy/app/controllers/api_controller.rb +7 -0
- data/test/dummy/app/controllers/application_controller.rb +9 -2
- data/test/dummy/app/controllers/exclusions_controller.rb +6 -2
- data/test/dummy/config.ru +3 -1
- data/test/dummy/config/application.rb +5 -4
- data/test/dummy/config/boot.rb +3 -1
- data/test/dummy/config/environment.rb +3 -1
- data/test/dummy/config/routes.rb +4 -0
- data/test/dummy/log/test.log +1381 -0
- data/test/test_helper.rb +16 -3
- metadata +70 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f310f1cd6d8278d5b0a669bb7123a802b68cfdcda1ec443de09b85a757c10eb3
|
4
|
+
data.tar.gz: 4eaec5f2dcf567dd0b8cf2c7374a0a1b95c965f3226204b729cfff7aa765f6ea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e44c7ab3ebfefe2dbcbe184243d03ba5f0eac7aa7b0b760aaf7cbd878468394cc519e7433bf72ef2dfd8ffcf3e0e099a18290035b86760e5143bf11a3236f42f
|
7
|
+
data.tar.gz: 57ce6512718fd75d5fbeb82e54577314b448b3c1b9e4714ad9b8b1021033501bc481e2d70a4f1b3c930727aa020a827535a357319e7a8928ba10e158d6ce8478
|
data/README.md
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
[![Gem Version](https://badge.fury.io/rb/angular_rails_csrf.svg)](https://badge.fury.io/rb/angular_rails_csrf)
|
4
4
|
[![Build Status](https://travis-ci.org/jsanders/angular_rails_csrf.svg)](https://travis-ci.org/jsanders/angular_rails_csrf)
|
5
|
+
[![Test Coverage](https://codecov.io/gh/jsanders/angular_rails_csrf/graph/badge.svg)](https://codecov.io/gh/jsanders/angular_rails_csrf)
|
5
6
|
|
6
7
|
The AngularJS [ng.$http](http://docs.angularjs.org/api/ng.$http) service has built-in CSRF protection. By default, it looks for a cookie named `XSRF-TOKEN` and, if found, writes its value into an `X-XSRF-TOKEN` header, which the server compares with the CSRF token saved in the user's session.
|
7
8
|
|
@@ -9,7 +10,7 @@ This project adds direct support for this scheme to your Rails application witho
|
|
9
10
|
|
10
11
|
Note that there is nothing AngularJS specific here, and this will work with any other front-end that implements the same scheme.
|
11
12
|
|
12
|
-
|
13
|
+
Check [version compatibility](https://github.com/jsanders/angular_rails_csrf/wiki/Version-Compatibility) to learn which Rails/Rubies are currently supported.
|
13
14
|
|
14
15
|
## Installation
|
15
16
|
|
@@ -51,6 +52,36 @@ end
|
|
51
52
|
|
52
53
|
If `angular_rails_csrf_domain` is not set, it defaults to `nil`.
|
53
54
|
|
55
|
+
### Secure Cookie
|
56
|
+
|
57
|
+
To set a "secure" flag for the cookie, set the `angular_rails_csrf_secure` option to `true`:
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
# application.rb
|
61
|
+
class Application < Rails::Application
|
62
|
+
#...
|
63
|
+
config.angular_rails_csrf_secure = true
|
64
|
+
end
|
65
|
+
```
|
66
|
+
|
67
|
+
`angular_rails_csrf_secure` defaults to `false`.
|
68
|
+
|
69
|
+
### SameSite
|
70
|
+
|
71
|
+
The SameSite attribute defaults to `:lax`. You can override this in the config:
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
# application.rb
|
75
|
+
class Application < Rails::Application
|
76
|
+
#...
|
77
|
+
config.angular_rails_csrf_same_site = :strict
|
78
|
+
end
|
79
|
+
```
|
80
|
+
|
81
|
+
**NOTE**: When using `config.angular_rails_csrf_same_site = :none`, this gem automatically sets the cookie to `Secure` (`config.angular_rails_csrf_secure = true`) to comply with [the specifications](https://tools.ietf.org/html/draft-west-cookie-incrementalism-00).
|
82
|
+
|
83
|
+
Please note that [Safari is known to have issues](https://bugs.webkit.org/show_bug.cgi?id=198181) with SameSite attribute set to `:none`.
|
84
|
+
|
54
85
|
### Exclusions
|
55
86
|
|
56
87
|
Sometimes you will want to skip setting the XSRF token for certain controllers (for example, when using SSE or ActionCable, as discussed [here](https://github.com/jsanders/angular_rails_csrf/issues/7)):
|
@@ -58,7 +89,7 @@ Sometimes you will want to skip setting the XSRF token for certain controllers (
|
|
58
89
|
```ruby
|
59
90
|
class ExclusionsController < ApplicationController
|
60
91
|
exclude_xsrf_token_cookie
|
61
|
-
|
92
|
+
|
62
93
|
# your actions here...
|
63
94
|
end
|
64
95
|
```
|
@@ -77,6 +108,6 @@ and then
|
|
77
108
|
$ rake test
|
78
109
|
```
|
79
110
|
|
80
|
-
## License
|
111
|
+
## License
|
81
112
|
|
82
113
|
Licensed under the [MIT License](https://github.com/jsanders/angular_rails_csrf/blob/master/LICENSE).
|
data/Rakefile
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
begin
|
2
4
|
require 'bundler/setup'
|
3
5
|
rescue LoadError
|
@@ -14,19 +16,14 @@ RDoc::Task.new(:rdoc) do |rdoc|
|
|
14
16
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
17
|
end
|
16
18
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
19
|
Bundler::GemHelper.install_tasks
|
21
20
|
|
22
21
|
require 'rake/testtask'
|
23
22
|
|
24
23
|
Rake::TestTask.new(:test) do |t|
|
25
|
-
t.libs
|
26
|
-
t.libs << 'test'
|
24
|
+
t.libs = %w[lib test]
|
27
25
|
t.pattern = 'test/**/*_test.rb'
|
28
26
|
t.verbose = false
|
29
27
|
end
|
30
28
|
|
31
|
-
|
32
29
|
task default: :test
|
data/lib/angular_rails_csrf.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module AngularRailsCsrf
|
2
4
|
module Concern
|
3
5
|
extend ActiveSupport::Concern
|
@@ -7,25 +9,49 @@ module AngularRailsCsrf
|
|
7
9
|
end
|
8
10
|
|
9
11
|
def set_xsrf_token_cookie
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
return unless defined?(protect_against_forgery?) && protect_against_forgery? && !respond_to?(:__exclude_xsrf_token_cookie?)
|
13
|
+
|
14
|
+
config = Rails.application.config
|
15
|
+
|
16
|
+
same_site = same_site_from config
|
17
|
+
secure = secure_from config
|
18
|
+
|
19
|
+
cookie_options = {
|
20
|
+
value: form_authenticity_token,
|
21
|
+
domain: domain_from(config),
|
22
|
+
same_site: same_site,
|
23
|
+
secure: same_site.eql?(:none) || secure
|
24
|
+
}
|
25
|
+
|
26
|
+
cookie_name = cookie_name_from config
|
27
|
+
cookies[cookie_name] = cookie_options
|
16
28
|
end
|
17
29
|
|
18
30
|
def verified_request?
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
31
|
+
super || valid_authenticity_token?(session, request.headers['X-XSRF-TOKEN'])
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def same_site_from(config)
|
37
|
+
config.respond_to?(:angular_rails_csrf_same_site) ? config.angular_rails_csrf_same_site : :lax
|
38
|
+
end
|
39
|
+
|
40
|
+
def secure_from(config)
|
41
|
+
config.angular_rails_csrf_secure if config.respond_to?(:angular_rails_csrf_secure)
|
42
|
+
end
|
43
|
+
|
44
|
+
def domain_from(config)
|
45
|
+
config.respond_to?(:angular_rails_csrf_domain) ? config.angular_rails_csrf_domain : nil
|
46
|
+
end
|
47
|
+
|
48
|
+
def cookie_name_from(config)
|
49
|
+
config.respond_to?(:angular_rails_csrf_cookie_name) ? config.angular_rails_csrf_cookie_name : 'XSRF-TOKEN'
|
24
50
|
end
|
25
51
|
|
26
52
|
module ClassMethods
|
27
53
|
def exclude_xsrf_token_cookie
|
28
|
-
|
54
|
+
class_eval do
|
29
55
|
def __exclude_xsrf_token_cookie?
|
30
56
|
true
|
31
57
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'test_helper'
|
2
4
|
|
3
5
|
class AngularRailsCsrfExceptionTest < ActionController::TestCase
|
@@ -8,9 +10,9 @@ class AngularRailsCsrfExceptionTest < ActionController::TestCase
|
|
8
10
|
@correct_token = @controller.send(:form_authenticity_token)
|
9
11
|
end
|
10
12
|
|
11
|
-
test
|
13
|
+
test 'a get does not set the XSRF-TOKEN cookie' do
|
12
14
|
get :index
|
13
15
|
assert_not_equal @correct_token, cookies['XSRF-TOKEN']
|
14
16
|
assert_response :success
|
15
17
|
end
|
16
|
-
end
|
18
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
class AngularRailsCsrfSkipTest < ActionController::TestCase
|
6
|
+
tests ApiController
|
7
|
+
|
8
|
+
test 'csrf-cookie is not set and no error if protect_against_forgery? is not defined' do
|
9
|
+
refute @controller.respond_to?(:protect_against_forgery?)
|
10
|
+
get :index
|
11
|
+
assert_nil cookies['XSRF-TOKEN']
|
12
|
+
assert_response :success
|
13
|
+
end
|
14
|
+
end
|
@@ -1,74 +1,140 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'test_helper'
|
2
4
|
|
3
5
|
class AngularRailsCsrfTest < ActionController::TestCase
|
4
6
|
tests ApplicationController
|
5
7
|
|
6
|
-
test
|
8
|
+
test 'a get sets the XSRF-TOKEN cookie but does not require the X-XSRF-TOKEN header' do
|
7
9
|
get :index
|
8
10
|
assert_valid_cookie
|
9
11
|
assert_response :success
|
10
12
|
end
|
11
13
|
|
12
|
-
test
|
14
|
+
test 'a post raises an error without the X-XSRF-TOKEN header set' do
|
13
15
|
assert_raises ActionController::InvalidAuthenticityToken do
|
14
16
|
post :create
|
15
17
|
end
|
16
18
|
end
|
17
19
|
|
18
|
-
test
|
19
|
-
|
20
|
+
test 'a post raises an error with the X-XSRF-TOKEN header set to the wrong value' do
|
21
|
+
header_to 'garbage'
|
20
22
|
assert_raises ActionController::InvalidAuthenticityToken do
|
21
23
|
post :create
|
22
24
|
end
|
23
25
|
end
|
24
26
|
|
25
|
-
test
|
26
|
-
|
27
|
+
test 'a post is accepted if X-XSRF-TOKEN is set properly' do
|
28
|
+
header_to @controller.send(:form_authenticity_token)
|
27
29
|
post :create
|
28
30
|
assert_valid_cookie
|
29
31
|
assert_response :success
|
30
32
|
end
|
31
33
|
|
32
|
-
test
|
34
|
+
test 'csrf-cookie is not set if exclusion is enabled' do
|
35
|
+
refute @controller.respond_to?(:__exclude_xsrf_token_cookie?)
|
36
|
+
@controller.class_eval { exclude_xsrf_token_cookie }
|
37
|
+
get :index
|
38
|
+
assert_valid_cookie present: false
|
39
|
+
assert @controller.__exclude_xsrf_token_cookie?
|
40
|
+
assert_response :success
|
41
|
+
end
|
42
|
+
|
43
|
+
test 'the domain is used if present' do
|
33
44
|
config = Rails.application.config
|
34
|
-
def config.angular_rails_csrf_domain
|
45
|
+
def config.angular_rails_csrf_domain
|
46
|
+
:all
|
47
|
+
end
|
35
48
|
|
36
49
|
get :index
|
37
50
|
assert @response.headers['Set-Cookie'].include?('.test.host')
|
38
51
|
assert_valid_cookie
|
39
52
|
assert_response :success
|
53
|
+
ensure
|
54
|
+
config.instance_eval('undef :angular_rails_csrf_domain', __FILE__, __LINE__)
|
55
|
+
end
|
56
|
+
|
57
|
+
test 'the secure flag is set if configured' do
|
58
|
+
@request.headers['HTTPS'] = 'on'
|
59
|
+
|
60
|
+
config = Rails.application.config
|
61
|
+
config.define_singleton_method(:angular_rails_csrf_secure) { true }
|
62
|
+
|
63
|
+
get :index
|
64
|
+
assert @response.headers['Set-Cookie'].include?('secure')
|
65
|
+
assert_valid_cookie
|
66
|
+
assert_response :success
|
67
|
+
ensure
|
68
|
+
@request.headers['HTTPS'] = nil
|
69
|
+
config.instance_eval('undef :angular_rails_csrf_secure', __FILE__, __LINE__)
|
40
70
|
end
|
41
71
|
|
42
|
-
test
|
72
|
+
test 'a custom name is used if present' do
|
43
73
|
use_custom_cookie_name do
|
44
74
|
get :index
|
45
75
|
assert @response.headers['Set-Cookie'].include?('CUSTOM-COOKIE-NAME')
|
46
|
-
assert_valid_cookie
|
76
|
+
assert_valid_cookie name: 'CUSTOM-COOKIE-NAME'
|
47
77
|
assert_response :success
|
48
78
|
end
|
49
79
|
end
|
50
80
|
|
81
|
+
test 'same_site is set to Lax by default' do
|
82
|
+
get :index
|
83
|
+
assert @response.headers['Set-Cookie'].include?('SameSite=Lax')
|
84
|
+
assert_valid_cookie
|
85
|
+
assert_response :success
|
86
|
+
end
|
87
|
+
|
88
|
+
test 'same_site can be configured' do
|
89
|
+
config = Rails.application.config
|
90
|
+
config.define_singleton_method(:angular_rails_csrf_same_site) { :strict }
|
91
|
+
|
92
|
+
get :index
|
93
|
+
assert @response.headers['Set-Cookie'].include?('SameSite=Strict')
|
94
|
+
assert_valid_cookie
|
95
|
+
assert_response :success
|
96
|
+
ensure
|
97
|
+
config.instance_eval('undef :angular_rails_csrf_same_site', __FILE__, __LINE__)
|
98
|
+
end
|
99
|
+
|
100
|
+
test 'secure is set automatically when same_site is set to none' do
|
101
|
+
@request.headers['HTTPS'] = 'on'
|
102
|
+
|
103
|
+
config = Rails.application.config
|
104
|
+
config.define_singleton_method(:angular_rails_csrf_same_site) { :none }
|
105
|
+
|
106
|
+
get :index
|
107
|
+
assert @response.headers['Set-Cookie'].include?('SameSite=None')
|
108
|
+
assert @response.headers['Set-Cookie'].include?('secure')
|
109
|
+
assert_valid_cookie
|
110
|
+
assert_response :success
|
111
|
+
ensure
|
112
|
+
config.instance_eval('undef :angular_rails_csrf_same_site', __FILE__, __LINE__)
|
113
|
+
end
|
114
|
+
|
51
115
|
private
|
52
116
|
|
53
117
|
# Helpers
|
54
118
|
|
55
|
-
def
|
119
|
+
def header_to(value)
|
56
120
|
@request.headers['X-XSRF-TOKEN'] = value
|
57
121
|
end
|
58
122
|
|
59
|
-
def assert_valid_cookie(name
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
assert_equal @controller.send(:form_authenticity_token), cookies['XSRF-TOKEN']
|
64
|
-
end
|
123
|
+
def assert_valid_cookie(name: 'XSRF-TOKEN', present: true)
|
124
|
+
cookie_valid = @controller.send(:valid_authenticity_token?, session, cookies[name])
|
125
|
+
cookie_valid = !cookie_valid unless present
|
126
|
+
assert cookie_valid
|
65
127
|
end
|
66
128
|
|
67
129
|
def use_custom_cookie_name
|
68
130
|
config = Rails.application.config
|
69
|
-
def config.angular_rails_csrf_cookie_name
|
131
|
+
def config.angular_rails_csrf_cookie_name
|
132
|
+
'CUSTOM-COOKIE-NAME'
|
133
|
+
end
|
70
134
|
yield
|
71
135
|
ensure
|
72
|
-
|
136
|
+
eval <<-RUBY, binding, __FILE__, __LINE__ + 1
|
137
|
+
config.instance_eval('undef :angular_rails_csrf_cookie_name')
|
138
|
+
RUBY
|
73
139
|
end
|
74
140
|
end
|
@@ -1,6 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class ApplicationController < ActionController::Base
|
2
4
|
protect_from_forgery with: :exception
|
3
5
|
|
4
|
-
def index
|
5
|
-
|
6
|
+
def index
|
7
|
+
head :ok
|
8
|
+
end
|
9
|
+
|
10
|
+
def create
|
11
|
+
head :ok
|
12
|
+
end
|
6
13
|
end
|
data/test/dummy/config.ru
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require File.expand_path('boot', __dir__)
|
4
|
+
|
5
|
+
require 'action_controller/railtie'
|
4
6
|
|
5
7
|
Bundler.require(:default, Rails.env)
|
6
|
-
require
|
8
|
+
require 'angular_rails_csrf'
|
7
9
|
|
8
10
|
module Dummy
|
9
11
|
class Application < Rails::Application
|
@@ -12,4 +14,3 @@ module Dummy
|
|
12
14
|
config.active_support.test_order = :random
|
13
15
|
end
|
14
16
|
end
|
15
|
-
|