angular_rails_csrf 4.2.0 → 5.0.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: 4e8a320d5c0af2703ea102297a762789112438a32347be78810f9ed0a38f0944
4
- data.tar.gz: 69b97e7968cc7969768432e5b37a73aae82525dc22aa4736aca1ef008f479568
3
+ metadata.gz: 777a87b5a6709b3c193f4205d7327790efa17a1a1d3686a8e49fc69b03e62e1b
4
+ data.tar.gz: b693aa26b63bc1772a8b14728414d3922e9a2c2a33be088f4f123ca06885c58a
5
5
  SHA512:
6
- metadata.gz: 064c1ad9b08d278ba698c7e0deebd6a0169aeba2f665c9ada2e67fe08cc704cd7d1d21cb79d31be3bf55d985d951988e87239b67ca03169ea6b6bbaf740423aa
7
- data.tar.gz: 9a6126ddfa20f2d162e569a277f539b5b15bceddc5f75f2321f8d2d5771b6b5de53c54a388397b91e7ee94d4a2bb08d812f01b230078f33d5d46884f7f67952a
6
+ metadata.gz: e0a0afe2adc0f5dd08d95c7776f219101a659e3ba4ad1ba4abc4392a5a8f6e70cb5c957174c443266665348be35d9a33936ec2b1aefa2abb6bf0cd7558933e72
7
+ data.tar.gz: 5bba0256727b1dd432178d5fe4405ae11d4591e2be1fee0d13ac9c4bdfe29108040b2247a3f7fa80d3f0d6c43a7e426ab47490db521b1f1ec659625ef397a18c
data/README.md CHANGED
@@ -1,8 +1,9 @@
1
1
  ## AngularJS-style CSRF Protection for Rails
2
2
 
3
- [![Gem Version](https://badge.fury.io/rb/angular_rails_csrf.svg)](https://badge.fury.io/rb/angular_rails_csrf)
4
- [![Build Status](https://travis-ci.org/jsanders/angular_rails_csrf.svg)](https://travis-ci.org/jsanders/angular_rails_csrf)
3
+ ![Gem](https://img.shields.io/gem/v/angular_rails_csrf)
4
+ [![Build Status](https://travis-ci.com/jsanders/angular_rails_csrf.svg?branch=master)](https://travis-ci.com/jsanders/angular_rails_csrf)
5
5
  [![Test Coverage](https://codecov.io/gh/jsanders/angular_rails_csrf/graph/badge.svg)](https://codecov.io/gh/jsanders/angular_rails_csrf)
6
+ ![Downloads total](https://img.shields.io/gem/dt/angular_rails_csrf)
6
7
 
7
8
  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.
8
9
 
@@ -16,11 +17,15 @@ Check [version compatibility](https://github.com/jsanders/angular_rails_csrf/wik
16
17
 
17
18
  Add this line to your application's *Gemfile*:
18
19
 
19
- gem 'angular_rails_csrf'
20
+ ```ruby
21
+ gem 'angular_rails_csrf'
22
+ ```
20
23
 
21
24
  And then execute:
22
25
 
23
- $ bundle
26
+ ```console
27
+ $ bundle
28
+ ```
24
29
 
25
30
  That's it!
26
31
 
@@ -80,6 +85,22 @@ end
80
85
 
81
86
  **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
87
 
88
+ Please note that [Safari is known to have issues](https://bugs.webkit.org/show_bug.cgi?id=198181) with SameSite attribute set to `:none`.
89
+
90
+ ### HttpOnly Cookie
91
+
92
+ To set the ["httponly" flag](https://owasp.org/www-community/HttpOnly) for your cookie, set the `angular_rails_csrf_httponly` option to `true`:
93
+
94
+ ```ruby
95
+ # application.rb
96
+ class Application < Rails::Application
97
+ #...
98
+ config.angular_rails_csrf_httponly = true
99
+ end
100
+ ```
101
+
102
+ `angular_rails_csrf_httponly` defaults to `false`.
103
+
83
104
  ### Exclusions
84
105
 
85
106
  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)):
data/Rakefile CHANGED
@@ -21,8 +21,7 @@ Bundler::GemHelper.install_tasks
21
21
  require 'rake/testtask'
22
22
 
23
23
  Rake::TestTask.new(:test) do |t|
24
- t.libs << 'lib'
25
- t.libs << 'test'
24
+ t.libs = %w[lib test]
26
25
  t.pattern = 'test/**/*_test.rb'
27
26
  t.verbose = false
28
27
  end
@@ -9,48 +9,37 @@ module AngularRailsCsrf
9
9
  end
10
10
 
11
11
  def set_xsrf_token_cookie
12
- return unless protect_against_forgery? && !respond_to?(:__exclude_xsrf_token_cookie?)
12
+ return unless defined?(protect_against_forgery?) && protect_against_forgery? && !respond_to?(:__exclude_xsrf_token_cookie?)
13
13
 
14
14
  config = Rails.application.config
15
15
 
16
- same_site = same_site_from config
17
- secure = secure_from config
16
+ secure = option_from config, :angular_rails_csrf_secure
17
+ same_site = option_from config, :angular_rails_csrf_same_site, :lax
18
18
 
19
19
  cookie_options = {
20
20
  value: form_authenticity_token,
21
- domain: domain_from(config),
21
+ domain: option_from(config, :angular_rails_csrf_domain),
22
22
  same_site: same_site,
23
+ httponly: option_from(config, :angular_rails_csrf_httponly, false),
23
24
  secure: same_site.eql?(:none) || secure
24
25
  }
25
26
 
26
- cookie_name = cookie_name_from config
27
+ cookie_name = option_from(config,
28
+ :angular_rails_csrf_cookie_name,
29
+ 'XSRF-TOKEN')
27
30
  cookies[cookie_name] = cookie_options
28
31
  end
29
32
 
30
33
  def verified_request?
31
- if respond_to?(:valid_authenticity_token?, true)
32
- super || valid_authenticity_token?(session, request.headers['X-XSRF-TOKEN'])
33
- else
34
- super || form_authenticity_token == request.headers['X-XSRF-TOKEN']
35
- end
34
+ super || valid_authenticity_token?(session, request.headers['X-XSRF-TOKEN'])
36
35
  end
37
36
 
38
37
  private
39
38
 
40
- def same_site_from(config)
41
- config.respond_to?(:angular_rails_csrf_same_site) ? config.angular_rails_csrf_same_site : :lax
42
- end
43
-
44
- def secure_from(config)
45
- config.angular_rails_csrf_secure if config.respond_to?(:angular_rails_csrf_secure)
46
- end
47
-
48
- def domain_from(config)
49
- config.respond_to?(:angular_rails_csrf_domain) ? config.angular_rails_csrf_domain : nil
50
- end
51
-
52
- def cookie_name_from(config)
53
- config.respond_to?(:angular_rails_csrf_cookie_name) ? config.angular_rails_csrf_cookie_name : 'XSRF-TOKEN'
39
+ # Fetches the given option from config
40
+ # If the option is not set, return a default value
41
+ def option_from(config, option, default = nil)
42
+ config.respond_to?(option) ? config.send(option) : default
54
43
  end
55
44
 
56
45
  module ClassMethods
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AngularRailsCsrf
4
- VERSION = '4.2.0'
4
+ VERSION = '5.0.0'
5
5
  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
@@ -31,48 +31,65 @@ class AngularRailsCsrfTest < ActionController::TestCase
31
31
  assert_response :success
32
32
  end
33
33
 
34
- test 'the domain is used if present' do
35
- begin
36
- config = Rails.application.config
37
- def config.angular_rails_csrf_domain
38
- :all
39
- end
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
40
42
 
41
- get :index
42
- assert @response.headers['Set-Cookie'].include?('.test.host')
43
- assert_valid_cookie
44
- assert_response :success
45
- ensure
46
- config.instance_eval('undef :angular_rails_csrf_domain', __FILE__, __LINE__)
43
+ test 'the domain is used if present' do
44
+ config = Rails.application.config
45
+ def config.angular_rails_csrf_domain
46
+ :all
47
47
  end
48
+
49
+ get :index
50
+ assert @response.headers['Set-Cookie'].include?('.test.host')
51
+ assert_valid_cookie
52
+ assert_response :success
53
+ ensure
54
+ config.instance_eval('undef :angular_rails_csrf_domain', __FILE__, __LINE__)
48
55
  end
49
56
 
50
57
  test 'the secure flag is set if configured' do
51
- begin
52
- @request.headers['HTTPS'] = 'on'
58
+ @request.headers['HTTPS'] = 'on'
53
59
 
54
- config = Rails.application.config
55
- config.define_singleton_method(:angular_rails_csrf_secure) { true }
60
+ config = Rails.application.config
61
+ config.define_singleton_method(:angular_rails_csrf_secure) { true }
56
62
 
57
- get :index
58
- assert @response.headers['Set-Cookie'].include?('secure')
59
- assert_valid_cookie
60
- assert_response :success
61
- ensure
62
- @request.headers['HTTPS'] = nil
63
- config.instance_eval('undef :angular_rails_csrf_secure', __FILE__, __LINE__)
64
- end
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__)
65
70
  end
66
71
 
67
72
  test 'a custom name is used if present' do
68
73
  use_custom_cookie_name do
69
74
  get :index
70
75
  assert @response.headers['Set-Cookie'].include?('CUSTOM-COOKIE-NAME')
71
- assert_valid_cookie('CUSTOM-COOKIE-NAME')
76
+ assert_valid_cookie name: 'CUSTOM-COOKIE-NAME'
72
77
  assert_response :success
73
78
  end
74
79
  end
75
80
 
81
+ test 'the httponly flag is set if configured' do
82
+ config = Rails.application.config
83
+ config.define_singleton_method(:angular_rails_csrf_httponly) { true }
84
+
85
+ get :index
86
+ assert @response.headers['Set-Cookie'].include?('HttpOnly')
87
+ assert_valid_cookie
88
+ assert_response :success
89
+ ensure
90
+ config.instance_eval('undef :angular_rails_csrf_httponly', __FILE__, __LINE__)
91
+ end
92
+
76
93
  test 'same_site is set to Lax by default' do
77
94
  get :index
78
95
  assert @response.headers['Set-Cookie'].include?('SameSite=Lax')
@@ -81,34 +98,30 @@ class AngularRailsCsrfTest < ActionController::TestCase
81
98
  end
82
99
 
83
100
  test 'same_site can be configured' do
84
- begin
85
- config = Rails.application.config
86
- config.define_singleton_method(:angular_rails_csrf_same_site) { :strict }
101
+ config = Rails.application.config
102
+ config.define_singleton_method(:angular_rails_csrf_same_site) { :strict }
87
103
 
88
- get :index
89
- assert @response.headers['Set-Cookie'].include?('SameSite=Strict')
90
- assert_valid_cookie
91
- assert_response :success
92
- ensure
93
- config.instance_eval('undef :angular_rails_csrf_same_site', __FILE__, __LINE__)
94
- end
104
+ get :index
105
+ assert @response.headers['Set-Cookie'].include?('SameSite=Strict')
106
+ assert_valid_cookie
107
+ assert_response :success
108
+ ensure
109
+ config.instance_eval('undef :angular_rails_csrf_same_site', __FILE__, __LINE__)
95
110
  end
96
111
 
97
112
  test 'secure is set automatically when same_site is set to none' do
98
- begin
99
- @request.headers['HTTPS'] = 'on'
113
+ @request.headers['HTTPS'] = 'on'
100
114
 
101
- config = Rails.application.config
102
- config.define_singleton_method(:angular_rails_csrf_same_site) { :none }
115
+ config = Rails.application.config
116
+ config.define_singleton_method(:angular_rails_csrf_same_site) { :none }
103
117
 
104
- get :index
105
- assert @response.headers['Set-Cookie'].include?('SameSite=None')
106
- assert @response.headers['Set-Cookie'].include?('secure')
107
- assert_valid_cookie
108
- assert_response :success
109
- ensure
110
- config.instance_eval('undef :angular_rails_csrf_same_site', __FILE__, __LINE__)
111
- end
118
+ get :index
119
+ assert @response.headers['Set-Cookie'].include?('SameSite=None')
120
+ assert @response.headers['Set-Cookie'].include?('secure')
121
+ assert_valid_cookie
122
+ assert_response :success
123
+ ensure
124
+ config.instance_eval('undef :angular_rails_csrf_same_site', __FILE__, __LINE__)
112
125
  end
113
126
 
114
127
  private
@@ -119,12 +132,10 @@ class AngularRailsCsrfTest < ActionController::TestCase
119
132
  @request.headers['X-XSRF-TOKEN'] = value
120
133
  end
121
134
 
122
- def assert_valid_cookie(name = 'XSRF-TOKEN')
123
- if @controller.respond_to?(:valid_authenticity_token?, true)
124
- assert @controller.send(:valid_authenticity_token?, session, cookies[name])
125
- else
126
- assert_equal @controller.send(:form_authenticity_token), cookies['XSRF-TOKEN']
127
- end
135
+ def assert_valid_cookie(name: 'XSRF-TOKEN', present: true)
136
+ cookie_valid = @controller.send(:valid_authenticity_token?, session, cookies[name])
137
+ cookie_valid = !cookie_valid unless present
138
+ assert cookie_valid
128
139
  end
129
140
 
130
141
  def use_custom_cookie_name
@@ -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
@@ -5,4 +5,6 @@ Dummy::Application.routes.draw do
5
5
  post 'test' => 'application#create'
6
6
 
7
7
  get 'exclusions' => 'exclusions#index'
8
+
9
+ get 'index' => 'api#index'
8
10
  end
data/test/dummy/config.ru CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  # This file is used by Rack-based servers to start the application.
4
4
 
5
- require ::File.expand_path('../config/environment', __FILE__)
5
+ require ::File.expand_path('config/environment', __dir__)
6
6
  run Rails.application