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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fe9fb037133e9bf60dcd922bd29936ab85b8c4c730cba28ac2602703ab24401d
4
- data.tar.gz: 652584f4e71bf641625437f9e5c596fc4d33737fd6381b2d30d9ab64aad00049
3
+ metadata.gz: f310f1cd6d8278d5b0a669bb7123a802b68cfdcda1ec443de09b85a757c10eb3
4
+ data.tar.gz: 4eaec5f2dcf567dd0b8cf2c7374a0a1b95c965f3226204b729cfff7aa765f6ea
5
5
  SHA512:
6
- metadata.gz: fcba72111050df7ee9184c1d12ad1ce1bc9c4f006d8fa51e6cfeaa11180609893fc44e055a1274ef631f7fefbee1afaf73217083a01725d5508ce34f263099c8
7
- data.tar.gz: 5387400b46c17e2e7edf9b426f45dc83763a43f634d4d8a1baccca4a4d09ae910e17392a7d3391cdfa464b6823908586180aba3d72b52fe7890e766c42bceacd
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
- *Version 3 supports only Rails 4+ and Ruby 2.3+. If you are still on Rails 3 (2, 1?!), you have to utilize version 2.1.1!*
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 << 'lib'
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
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'angular_rails_csrf/railtie'
@@ -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
- if protect_against_forgery? && !respond_to?(:__exclude_xsrf_token_cookie?)
11
- config = Rails.application.config
12
- domain = config.respond_to?(:angular_rails_csrf_domain) ? config.angular_rails_csrf_domain : nil
13
- cookie_name = config.respond_to?(:angular_rails_csrf_cookie_name) ? config.angular_rails_csrf_cookie_name : 'XSRF-TOKEN'
14
- cookies[cookie_name] = { value: form_authenticity_token, domain: domain }
15
- end
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
- if respond_to?(:valid_authenticity_token?, true)
20
- super || valid_authenticity_token?(session, request.headers['X-XSRF-TOKEN'])
21
- else
22
- super || form_authenticity_token == request.headers['X-XSRF-TOKEN']
23
- end
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
- self.class_eval do
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 'angular_rails_csrf/concern'
2
4
 
3
5
  module AngularRailsCsrf
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module AngularRailsCsrf
2
- VERSION = '4.0.0'.freeze
4
+ VERSION = '4.4.0'
3
5
  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 "a get does not set the XSRF-TOKEN cookie" do
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 "a get sets the XSRF-TOKEN cookie but does not require the X-XSRF-TOKEN header" do
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 "a post raises an error without the X-XSRF-TOKEN header set" do
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 "a post raises an error with the X-XSRF-TOKEN header set to the wrong value" do
19
- set_header_to 'garbage'
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 "a post is accepted if X-XSRF-TOKEN is set properly" do
26
- set_header_to @controller.send(:form_authenticity_token)
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 "the domain is used if present" do
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; :all; end
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 "a custom name is used if present" do
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('CUSTOM-COOKIE-NAME')
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 set_header_to(value)
119
+ def header_to(value)
56
120
  @request.headers['X-XSRF-TOKEN'] = value
57
121
  end
58
122
 
59
- def assert_valid_cookie(name = 'XSRF-TOKEN')
60
- if @controller.respond_to?(:valid_authenticity_token?, true)
61
- assert @controller.send(:valid_authenticity_token?, session, cookies[name])
62
- else
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; 'CUSTOM-COOKIE-NAME'; end
131
+ def config.angular_rails_csrf_cookie_name
132
+ 'CUSTOM-COOKIE-NAME'
133
+ end
70
134
  yield
71
135
  ensure
72
- config.instance_eval('undef :angular_rails_csrf_cookie_name')
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
@@ -0,0 +1,4 @@
1
+ //= link_tree ../images
2
+ //= link_tree ../fonts
3
+ //= link_directory ../javascripts .js
4
+ //= link_directory ../stylesheets .css
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ApiController < ActionController::API
4
+ def index
5
+ head :ok
6
+ end
7
+ 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; head :ok; end
5
- def create; head :ok; end
6
+ def index
7
+ head :ok
8
+ end
9
+
10
+ def create
11
+ head :ok
12
+ end
6
13
  end
@@ -1,5 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ExclusionsController < ApplicationController
2
4
  exclude_xsrf_token_cookie
3
5
 
4
- def index; head :ok; end
5
- end
6
+ def index
7
+ head :ok
8
+ end
9
+ end
@@ -1,4 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is used by Rack-based servers to start the application.
2
4
 
3
- require ::File.expand_path('../config/environment', __FILE__)
5
+ require ::File.expand_path('config/environment', __dir__)
4
6
  run Rails.application
@@ -1,9 +1,11 @@
1
- require File.expand_path('../boot', __FILE__)
1
+ # frozen_string_literal: true
2
2
 
3
- require "action_controller/railtie"
3
+ require File.expand_path('boot', __dir__)
4
+
5
+ require 'action_controller/railtie'
4
6
 
5
7
  Bundler.require(:default, Rails.env)
6
- require "angular_rails_csrf"
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
-