invisible_captcha 0.10.0 → 2.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
- SHA1:
3
- metadata.gz: 0b8877b6b9e63a8e3469df1b1e4d564c323798f4
4
- data.tar.gz: aa93c8cd7004683493f06ba0513bc4cce274f261
2
+ SHA256:
3
+ metadata.gz: 86acfe71e903568702c63c261bfd32a066dbc947d1c2fa2b4956178a7de591db
4
+ data.tar.gz: a9a8768c5bcfb9a7656e0638dde4bf40e169719385bf947dace7fa4d34f03656
5
5
  SHA512:
6
- metadata.gz: 32dca9e9c96528181b6665854d07b2edba9441390613824975617f26d263e747475688979224f09d6b582c3c762fa4f438517924534f361ffc7d266ca78d3d39
7
- data.tar.gz: f8a13ba2c4885e921668617788d751050ae47a86714c6537a645fbb40071996634a603f31f4a6ff1cdbe723bfc2dbf940a9f82874306baaf98728ea1f8c23cba
6
+ metadata.gz: ec1ed1b9f7bef2e753f7b736dbce8124b5cd4c699e4075cbd15fdade99e6f332025e3088f0754315b18f8bde48b7e883c3470f840a535a3631f2e5c659c415df
7
+ data.tar.gz: 3698ed54f31c8f87730dcd92e14c2076e10aaa98a14a765e7d3be8bfd0e259385572b5d3252b8328fb4a539634bb8e086270b2e7e34117ab76a15ab4f42c095b
data/.gitignore CHANGED
@@ -8,3 +8,4 @@ Gemfile.lock
8
8
  spec/dummy/log/*.log
9
9
  spec/dummy/tmp/
10
10
  .byebug_history
11
+ .DS_Store
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.travis.yml CHANGED
@@ -1,31 +1,18 @@
1
1
  language: ruby
2
2
  cache: bundler
3
- sudo: false
4
3
  rvm:
5
4
  - ruby-head
6
- - 2.4.2
7
- - 2.3.5
8
- - 2.2.8
9
- - 2.1.10
5
+ - 3.0.0
6
+ - 2.7.2
7
+ - 2.6.5
8
+ - 2.5.8
10
9
  gemfile:
11
- - gemfiles/rails_5.1.gemfile
12
- - gemfiles/rails_5.0.gemfile
13
- - gemfiles/rails_4.2.gemfile
14
- - gemfiles/rails_4.1.gemfile
15
- - gemfiles/rails_3.2.gemfile
10
+ - gemfiles/rails_6.1.gemfile
11
+ - gemfiles/rails_6.0.gemfile
12
+ - gemfiles/rails_5.2.gemfile
16
13
  matrix:
17
14
  exclude:
18
- - rvm: 2.1.10
19
- gemfile: gemfiles/rails_5.0.gemfile
20
- - rvm: 2.1.10
21
- gemfile: gemfiles/rails_5.1.gemfile
22
- - rvm: 2.4.2
23
- gemfile: gemfiles/rails_4.1.gemfile
24
- - rvm: 2.4.2
25
- gemfile: gemfiles/rails_3.2.gemfile
26
- - rvm: ruby-head
27
- gemfile: gemfiles/rails_4.1.gemfile
28
- - rvm: ruby-head
29
- gemfile: gemfiles/rails_3.2.gemfile
15
+ - rvm: 3.0.0
16
+ gemfile: gemfiles/rails_5.2.gemfile
30
17
  allow_failures:
31
- - rvm: ruby-head
18
+ - rvm: ruby-head
data/Appraisals CHANGED
@@ -1,19 +1,11 @@
1
- appraise "rails-5.1" do
2
- gem "rails", "~> 5.1.0"
1
+ appraise "rails-6.1" do
2
+ gem "rails", "~> 6.1.0"
3
3
  end
4
4
 
5
- appraise "rails-5.0" do
6
- gem "rails", "~> 5.0.0"
5
+ appraise "rails-6.0" do
6
+ gem "rails", "~> 6.0.0"
7
7
  end
8
8
 
9
- appraise "rails-4.2" do
10
- gem "rails", github: 'rails/rails', branch: '4-2-stable'
9
+ appraise "rails-5.2" do
10
+ gem "rails", "~> 5.2.0"
11
11
  end
12
-
13
- appraise "rails-4.1" do
14
- gem "rails", "~> 4.1.0"
15
- end
16
-
17
- appraise "rails-3.2" do
18
- gem "rails", "~> 3.2.0"
19
- end
data/CHANGELOG.md ADDED
@@ -0,0 +1,151 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [2.0.0]
6
+
7
+ - New spinner, IP based, validation check (#89)
8
+ - Drop official support for unmaintained Rails versions: 5.1, 5.0 and 4.2 (#86)
9
+ - Drop official support for EOL Rubies: 2.4 and 2.3 (#86)
10
+
11
+ ## [1.1.0]
12
+
13
+ - New option `prepend: true` for the controller macro (#77)
14
+
15
+ ## [1.0.1]
16
+
17
+ - Fix naming issue with Ruby 2.7 (#65)
18
+
19
+ ## [1.0.0]
20
+
21
+ - Remove Ruby 2.2 and Rails 3.2 support
22
+ - Add Instrumentation event (#62)
23
+
24
+ ## [0.13.0]
25
+
26
+ - Add support for the Content Security Policy nonce (#61)
27
+ - Freeze all strings (#60)
28
+
29
+ ## [0.12.2]
30
+
31
+ - Allow new timestamp to be set during `on_timestamp_spam` callback (#53)
32
+
33
+ ## [0.12.1]
34
+
35
+ - Clear timestamp stored in `session[:invisible_captcha_timestamp]` (#50)
36
+ - Rails 6 support
37
+
38
+ ## [0.12.0]
39
+
40
+ - Honeypot input with autocomplete="off" by default (#42)
41
+
42
+ ## [0.11.0]
43
+
44
+ - Improve logging (#40, #41)
45
+ - Official Rails 5.2 support
46
+ - Drop Ruby 2.1 from CI
47
+
48
+ ## [0.10.0]
49
+
50
+ - New timestamp on each request to avoid stale timestamps (#24)
51
+ - Allow to inject styles manually anywhere in the layout (#27)
52
+ - Allow to change threshold per action
53
+ - Dynamic css strategy to hide the honeypot
54
+ - Remove Ruby 1.9 support
55
+ - Random default honeypots on each restart
56
+ - Allow to pass html_options to honeypot input (#28)
57
+ - Improvements on demo application and tests
58
+ - Better strong parameters interaction (#30, #33)
59
+
60
+ ## [0.9.3]
61
+
62
+ - Rails 5.1 support (#29)
63
+ - Modernize CI Rubies
64
+
65
+ ## [0.9.2]
66
+
67
+ - Rails 5.0 official support (#23)
68
+ - Travis CI matrix improvements
69
+
70
+ ## [0.9.1]
71
+
72
+ - Add option (`timestamp_enabled`) to disable timestamp check (#22)
73
+
74
+ ## [0.9.0]
75
+
76
+ - Remove model style validations (#14)
77
+ - Consider as spam if timestamp not in session (#11)
78
+ - Allow to define a different threshold per action (#8)
79
+ - Appraisals integration (#8)
80
+ - CI improvements: use new Travis infrastructure (#8)
81
+
82
+ ## [0.8.2]
83
+
84
+ - Default timestamp action redirects to back (#19)
85
+ - Stores timestamps as string in session (#17)
86
+
87
+ ## [0.8.1]
88
+
89
+ - Time-sensitive form submissions (#7)
90
+ - I18n integration (#13)
91
+
92
+ ## [0.8.0]
93
+
94
+ - Better Rails integration with `ActiveSupport.on_load` callbacks (#5)
95
+ - Allow to override settings via the view helper (#5)
96
+
97
+ ## [0.7.0]
98
+
99
+ - Revamped code base to allow more customizations (#2)
100
+ - Added basic specs (#2)
101
+ - Travis integration (#2)
102
+ - Demo app (#2)
103
+
104
+ ## [0.6.5]
105
+
106
+ - Stop using Jeweler
107
+
108
+ ## [0.6.4]
109
+
110
+ - Docs! (#1)
111
+
112
+ ## [0.6.3]
113
+
114
+ - Internal re-naming
115
+
116
+ ## [0.6.2]
117
+
118
+ - Fix gem initialization
119
+
120
+ ## [0.6.0]
121
+
122
+ - Allow to configure via `InvisibleCaptcha.setup` block
123
+
124
+ ## [0.5.0]
125
+
126
+ - First version of controller filters
127
+
128
+ [2.0.0]: https://github.com/markets/invisible_captcha/compare/v1.1.0...v2.0.0
129
+ [1.1.0]: https://github.com/markets/invisible_captcha/compare/v1.0.1...v1.1.0
130
+ [1.0.1]: https://github.com/markets/invisible_captcha/compare/v1.0.0...v1.0.1
131
+ [1.0.0]: https://github.com/markets/invisible_captcha/compare/v0.13.0...v1.0.0
132
+ [0.13.0]: https://github.com/markets/invisible_captcha/compare/v0.12.2...v0.13.0
133
+ [0.12.2]: https://github.com/markets/invisible_captcha/compare/v0.12.1...v0.12.2
134
+ [0.12.1]: https://github.com/markets/invisible_captcha/compare/v0.12.0...v0.12.1
135
+ [0.12.0]: https://github.com/markets/invisible_captcha/compare/v0.11.0...v0.12.0
136
+ [0.11.0]: https://github.com/markets/invisible_captcha/compare/v0.10.0...v0.11.0
137
+ [0.10.0]: https://github.com/markets/invisible_captcha/compare/v0.9.3...v0.10.0
138
+ [0.9.3]: https://github.com/markets/invisible_captcha/compare/v0.9.2...v0.9.3
139
+ [0.9.2]: https://github.com/markets/invisible_captcha/compare/v0.9.1...v0.9.2
140
+ [0.9.1]: https://github.com/markets/invisible_captcha/compare/v0.9.0...v0.9.1
141
+ [0.9.0]: https://github.com/markets/invisible_captcha/compare/v0.8.2...v0.9.0
142
+ [0.8.2]: https://github.com/markets/invisible_captcha/compare/v0.8.1...v0.8.2
143
+ [0.8.1]: https://github.com/markets/invisible_captcha/compare/v0.8.0...v0.8.1
144
+ [0.8.0]: https://github.com/markets/invisible_captcha/compare/v0.7.0...v0.8.0
145
+ [0.7.0]: https://github.com/markets/invisible_captcha/compare/v0.6.5...v0.7.0
146
+ [0.6.5]: https://github.com/markets/invisible_captcha/compare/v0.6.4...v0.6.5
147
+ [0.6.4]: https://github.com/markets/invisible_captcha/compare/v0.6.3...v0.6.4
148
+ [0.6.3]: https://github.com/markets/invisible_captcha/compare/v0.6.2...v0.6.3
149
+ [0.6.2]: https://github.com/markets/invisible_captcha/compare/v0.6.0...v0.6.2
150
+ [0.6.0]: https://github.com/markets/invisible_captcha/compare/v0.5.0...v0.6.0
151
+ [0.5.0]: https://github.com/markets/invisible_captcha/compare/v0.4.1...v0.5.0
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  gemspec
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2012-2017 Marc Anguera Insa
1
+ Copyright 2012-2021 Marc Anguera Insa
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -1,37 +1,34 @@
1
1
  # Invisible Captcha
2
2
 
3
- [![Gem Version](https://badge.fury.io/rb/invisible_captcha.svg)](http://badge.fury.io/rb/invisible_captcha) [![Build Status](https://travis-ci.org/markets/invisible_captcha.svg)](https://travis-ci.org/markets/invisible_captcha)
3
+ [![Gem](https://img.shields.io/gem/v/invisible_captcha.svg?style=flat-square)](https://rubygems.org/gems/invisible_captcha)
4
+ [![Build Status](https://travis-ci.com/markets/invisible_captcha.svg?branch=master)](https://travis-ci.com/markets/invisible_captcha)
4
5
 
5
- > Simple and flexible spam protection solution for Rails applications.
6
+ > Complete and flexible spam protection solution for Rails applications.
6
7
 
7
8
  Invisible Captcha provides different techniques to protect your application against spambots.
8
9
 
9
- The main protection is a solution based on the `honeypot` principle, which provides a better user experience, since there is no extra steps for real users, but for the bots.
10
+ The main protection is a solution based on the `honeypot` principle, which provides a better user experience since there are no extra steps for real users, only for the bots.
10
11
 
11
12
  Essentially, the strategy consists on adding an input field :honey_pot: into the form that:
12
13
 
13
- * shouldn't be visible by the real users
14
- * should be left empty by the real users
15
- * will most be filled by spam bots
14
+ - shouldn't be visible by the real users
15
+ - should be left empty by the real users
16
+ - will most likely be filled by spam bots
16
17
 
17
- It also comes with a time-sensitive :hourglass: form submission.
18
+ It also comes with:
19
+ - a time-sensitive :hourglass: form submission
20
+ - an IP based :mag: spinner validation
18
21
 
19
22
  ## Installation
20
23
 
21
- Invisible Captcha is tested against Rails `>= 3.2` and Ruby `>= 2.1`.
24
+ Invisible Captcha is tested against Rails `>= 5.2` and Ruby `>= 2.5`.
22
25
 
23
- Add this line to you Gemfile:
26
+ Add this line to your Gemfile and then execute `bundle install`:
24
27
 
25
- ```
28
+ ```ruby
26
29
  gem 'invisible_captcha'
27
30
  ```
28
31
 
29
- Or install the gem manually:
30
-
31
- ```
32
- $ gem install invisible_captcha
33
- ```
34
-
35
32
  ## Usage
36
33
 
37
34
  View code:
@@ -52,7 +49,7 @@ class TopicsController < ApplicationController
52
49
  end
53
50
  ```
54
51
 
55
- This method will act as a `before_action` that triggers when spam is detected (honeypot field has some value). By default it responds with no content (only headers: `head(200)`). This is a good default, since the bot will surely read the response code and will think that it has achieved to submit the form properly. But, anyway, you are able to define your own callback by passing a method to the `on_spam` option:
52
+ This method will act as a `before_action` that triggers when spam is detected (honeypot field has some value). By default, it responds with no content (only headers: `head(200)`). This is a good default, since the bot will surely read the response code and will think that it has achieved to submit the form properly. But, anyway, you can define your own callback by passing a method to the `on_spam` option:
56
53
 
57
54
  ```ruby
58
55
  class TopicsController < ApplicationController
@@ -66,7 +63,7 @@ class TopicsController < ApplicationController
66
63
  end
67
64
  ```
68
65
 
69
- Note that is not mandatory to specify a `honeypot` attribute (nor in the view, nor in the controller). In this case, the engine will take a random field from `InvisibleCaptcha.honeypots`. So, if you're integrating it following this path, in your form:
66
+ Note that it is not mandatory to specify a `honeypot` attribute (neither in the view nor in the controller). In this case, the engine will take a random field from `InvisibleCaptcha.honeypots`. So, if you're integrating it following this path, in your form:
70
67
 
71
68
  ```erb
72
69
  <%= form_tag(new_contact_path) do |f| %>
@@ -74,12 +71,32 @@ Note that is not mandatory to specify a `honeypot` attribute (nor in the view, n
74
71
  <% end %>
75
72
  ```
76
73
 
77
- In you controller:
74
+ In your controller:
78
75
 
79
76
  ```
80
77
  invisible_captcha only: [:new_contact]
81
78
  ```
82
79
 
80
+ `invisible_captcha` sends all messages to `flash[:error]`. For messages to appear on your pages, add `<%= flash[:error] %>` to `app/views/layouts/application.html.erb` (somewhere near the top of your `<body>` element):
81
+
82
+ ```erb
83
+ <!DOCTYPE html>
84
+ <html>
85
+ <head>
86
+ <title>Yet another Rails app</title>
87
+ <%= stylesheet_link_tag "application", media: "all" %>
88
+ <%= javascript_include_tag "application" %>
89
+ <%= csrf_meta_tags %>
90
+ </head>
91
+ <body>
92
+ <%= flash[:error] %>
93
+ <%= yield %>
94
+ </body>
95
+ </html>
96
+ ```
97
+
98
+ You can place `<%= flash[:error] %>` next to `:alert` and `:notice` message types, if you have them in your `app/views/layouts/application.html.erb`.
99
+
83
100
  ## Options and customization
84
101
 
85
102
  This section contains a description of all plugin options and customizations.
@@ -88,13 +105,15 @@ This section contains a description of all plugin options and customizations.
88
105
 
89
106
  You can customize:
90
107
 
91
- * `sentence_for_humans`: text for real users if input field was visible. By default, it uses I18n (see below).
92
- * `honeypots`: collection of default honeypots. Used by the view helper, called with no args, to generate a random honeypot field name. By default, a random collection is already generated.
93
- * `visual_honeypots`: make honeypots visible, also useful to test/debug your implementation.
94
- * `timestamp_threshold`: fastest time (in seconds) to expect a human to submit the form (see [original article by Yoav Aner](http://blog.gingerlime.com/2012/simple-detection-of-comment-spam-in-rails/) outlining the idea). By default, 4 seconds. **NOTE:** It's recommended to deactivate the autocomplete feature to avoid false positives (`autocomplete="off"`).
95
- * `timestamp_enabled`: option to disable the time threshold check at application level. Could be useful, for example, on some testing scenarios. By default, true.
96
- * `timestamp_error_message`: flash error message thrown when form submitted quicker than the `timestamp_threshold` value. It uses I18n by default.
97
- * `injectable_styles`: if enabled, you should call anywhere in your layout the following helper `<%= invisible_captcha_styles %>`. This allows you to inject styles, for example, in `<head>`. False by default, styles are injected inline with the honeypot.
108
+ - `sentence_for_humans`: text for real users if input field was visible. By default, it uses I18n (see below).
109
+ - `honeypots`: collection of default honeypots. Used by the view helper, called with no args, to generate a random honeypot field name. By default, a random collection is already generated. As the random collection is stored in memory, it will not work if you are running multiple Rails instances behind a load balancer. See [Multiple Rails instances](#multiple-rails-instances).
110
+ - `visual_honeypots`: make honeypots visible, also useful to test/debug your implementation.
111
+ - `timestamp_threshold`: fastest time (in seconds) to expect a human to submit the form (see [original article by Yoav Aner](https://blog.gingerlime.com/2012/simple-detection-of-comment-spam-in-rails/) outlining the idea). By default, 4 seconds. **NOTE:** It's recommended to deactivate the autocomplete feature to avoid false positives (`autocomplete="off"`).
112
+ - `timestamp_enabled`: option to disable the time threshold check at application level. Could be useful, for example, on some testing scenarios. By default, true.
113
+ - `timestamp_error_message`: flash error message thrown when form submitted quicker than the `timestamp_threshold` value. It uses I18n by default.
114
+ - `injectable_styles`: if enabled, you should call anywhere in your layout the following helper `<%= invisible_captcha_styles %>`. This allows you to inject styles, for example, in `<head>`. False by default, styles are injected inline with the honeypot.
115
+ - `spinner_enabled`: option to disable the IP spinner validation.
116
+ - `secret`: customize the secret key to encode some internal values. By default, it reads the environment variable `ENV['INVISIBLE_CAPTCHA_SECRET']` and fallbacks to random value. Be careful, if you are running multiple Rails instances behind a load balancer, use always the same value via the environment variable.
98
117
 
99
118
  To change these defaults, add the following to an initializer (recommended `config/initializers/invisible_captcha.rb`):
100
119
 
@@ -102,9 +121,10 @@ To change these defaults, add the following to an initializer (recommended `conf
102
121
  InvisibleCaptcha.setup do |config|
103
122
  # config.honeypots << ['more', 'fake', 'attribute', 'names']
104
123
  # config.visual_honeypots = false
105
- # config.timestamp_threshold = 4
124
+ # config.timestamp_threshold = 2
106
125
  # config.timestamp_enabled = true
107
126
  # config.injectable_styles = false
127
+ # config.spinner_enabled = true
108
128
 
109
129
  # Leave these unset if you want to use I18n (see below)
110
130
  # config.sentence_for_humans = 'If you are a human, ignore this field'
@@ -112,28 +132,52 @@ InvisibleCaptcha.setup do |config|
112
132
  end
113
133
  ```
114
134
 
135
+ #### Multiple Rails instances
136
+
137
+ If you have multiple Rails instances running behind a load balancer, you have to share the same honeypots collection between the instances.
138
+
139
+ Either use a fixed collection or share them between the instances using `Rails.cache`:
140
+
141
+ ```ruby
142
+ InvisibleCaptcha.setup do |config|
143
+ config.honeypots = Rails.cache.fetch('invisible_captcha_honeypots') do
144
+ (1..20).map { InvisibleCaptcha.generate_random_honeypot }
145
+ end
146
+ end
147
+ ```
148
+
149
+ Be careful also with the `secret` setting. Since it will be stored in-memory, if you are running this setup, the best idea is to provide the environment variable (`ENV['INVISIBLE_CAPTCHA_SECRET']`) from your infrastructure.
150
+
115
151
  ### Controller method options:
116
152
 
117
153
  The `invisible_captcha` method accepts some options:
118
154
 
119
- * `only`: apply to given controller actions.
120
- * `except`: exclude to given controller actions.
121
- * `honeypot`: name of custom honeypot.
122
- * `scope`: name of scope, ie: 'topic[subtitle]' -> 'topic' is the scope.
123
- * `on_spam`: custom callback to be called on spam detection.
124
- * `timestamp_threshold`: enable/disable this technique at action level.
125
- * `on_timestamp_spam`: custom callback to be called when form submitted too quickly. The default action redirects to `:back` printing a warning in `flash[:error]`.
126
- * `timestamp_threshold`: custom threshold per controller/action. Overrides the global value for `InvisibleCaptcha.timestamp_threshold`.
155
+ - `only`: apply to given controller actions.
156
+ - `except`: exclude to given controller actions.
157
+ - `honeypot`: name of custom honeypot.
158
+ - `scope`: name of scope, ie: 'topic[subtitle]' -> 'topic' is the scope. By default, it's inferred from the `controller_name`.
159
+ - `on_spam`: custom callback to be called on spam detection.
160
+ - `timestamp_enabled`: enable/disable this technique at action level.
161
+ - `on_timestamp_spam`: custom callback to be called when form submitted too quickly. The default action redirects to `:back` printing a warning in `flash[:error]`.
162
+ - `timestamp_threshold`: custom threshold per controller/action. Overrides the global value for `InvisibleCaptcha.timestamp_threshold`.
163
+ - `prepend`: the spam detection will run in a `prepend_before_action` if `prepend: true`, otherwise will run in a `before_action`.
127
164
 
128
165
  ### View helpers options:
129
166
 
130
- Using the view/form helper you can override some defaults for the given instance. Actually, it allows to change: `sentence_for_humans` and `visual_honeypots`.
167
+ Using the view/form helper you can override some defaults for the given instance. Actually, it allows to change:
168
+
169
+ - `sentence_for_humans`
131
170
 
132
171
  ```erb
133
172
  <%= form_for(@topic) do |f| %>
134
- <%= f.invisible_captcha :subtitle, visual_honeypots: true, sentence_for_humans: "hey! leave this input empty!" %>
135
- <!-- or -->
136
- <%= invisible_captcha visual_honeypots: true, sentence_for_humans: "hey! leave this input empty!" %>
173
+ <%= f.invisible_captcha :subtitle, sentence_for_humans: "hey! leave this input empty!" %>
174
+ <% end %>
175
+ ```
176
+ - `visual_honeypots`
177
+
178
+ ```erb
179
+ <%= form_for(@topic) do |f| %>
180
+ <%= f.invisible_captcha :subtitle, visual_honeypots: true %>
137
181
  <% end %>
138
182
  ```
139
183
 
@@ -143,6 +187,71 @@ You can also pass html options to the input:
143
187
  <%= invisible_captcha :subtitle, :topic, id: "your_id", class: "your_class" %>
144
188
  ```
145
189
 
190
+ ### Spam detection notifications
191
+
192
+ In addition to the `on_spam` controller callback, you can use the [Active Support Instrumentation API](https://guides.rubyonrails.org/active_support_instrumentation.html) to set up a global event handler that fires whenever spam is detected. This is useful for advanced logging, background processing, etc.
193
+
194
+ To set up a global event handler, [subscribe](https://guides.rubyonrails.org/active_support_instrumentation.html#subscribing-to-an-event) to the `invisible_captcha.spam_detected` event in an initializer:
195
+
196
+ ```ruby
197
+ # config/initializers/invisible_captcha.rb
198
+
199
+ ActiveSupport::Notifications.subscribe('invisible_captcha.spam_detected') do |*args, data|
200
+ AwesomeLogger.warn(data[:message], data) # Log to an external logging service.
201
+ SpamRequest.create(data) # Record the blocked request in your database.
202
+ end
203
+ ```
204
+
205
+ The `data` passed to the subscriber is hash containing information about the request that was detected as spam. For example:
206
+
207
+ ```ruby
208
+ {
209
+ message: "Invisible Captcha honeypot param 'subtitle' was present.",
210
+ remote_ip: '127.0.0.1',
211
+ user_agent: 'Chrome 77',
212
+ controller: 'users',
213
+ action: 'create',
214
+ url: 'http://example.com/users',
215
+ params: {
216
+ topic: { subtitle: 'foo' },
217
+ controller: 'users',
218
+ action: 'create'
219
+ }
220
+ }
221
+ ```
222
+
223
+ _**Note:** `params` will be filtered according to your `Rails.application.config.filter_parameters` configuration, making them (probably) safe for logging. But always double-check that you're not inadvertently logging sensitive form data, like passwords and credit cards._
224
+
225
+ ### Content Security Policy
226
+
227
+ If you're using a Content Security Policy (CSP) in your Rails app, you will need to generate a nonce on the server, and pass `nonce: true` attribute to the view helper. Uncomment the following lines in your `config/initializers/content_security_policy.rb` file:
228
+
229
+ ```ruby
230
+ # Be sure to restart your server when you modify this file.
231
+
232
+ # If you are using UJS then enable automatic nonce generation
233
+ Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
234
+
235
+ # Set the nonce only to specific directives
236
+ Rails.application.config.content_security_policy_nonce_directives = %w(style-src)
237
+ ```
238
+ Note that if you are already generating nonce for scripts, you'd have to include `script-src` to `content_security_policy_nonce_directives` as well:
239
+
240
+ ```ruby
241
+ Rails.application.config.content_security_policy_nonce_directives = %w(script-src style-src)
242
+ ```
243
+
244
+ And in your view helper, you need to pass `nonce: true` to the `invisible_captcha` helper:
245
+
246
+ ```erb
247
+ <%= invisible_captcha nonce: true %>
248
+ ```
249
+
250
+ **WARNING:** Content Security Policy can break your site! If you already run a website with third-party scripts, styles, images, and fonts, it is highly recommended to enable CSP in report-only mode and observe warnings as they appear. Learn more at MDN:
251
+
252
+ * https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
253
+ * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
254
+
146
255
  ### I18n
147
256
 
148
257
  `invisible_captcha` tries to use I18n when it's available by default. The keys it looks for are the following:
@@ -154,10 +263,33 @@ en:
154
263
  timestamp_error_message: "Sorry, that was too quick! Please resubmit."
155
264
  ```
156
265
 
157
- You can override the english ones in your own i18n config files as well as add new ones for other locales.
266
+ You can override the English ones in your i18n config files as well as add new ones for other locales.
158
267
 
159
268
  If you intend to use I18n with `invisible_captcha`, you _must not_ set `sentence_for_humans` or `timestamp_error_message` to strings in the setup phase.
160
269
 
270
+ ## Testing your controllers
271
+
272
+ If you're encountering unexpected behaviour while testing controllers that use the `invisible_captcha` action filter, you may want to disable timestamp check for the test environment. Add the following snippet to the `config/initializers/invisible_captcha.rb` file:
273
+
274
+ ```ruby
275
+ # Be sure to restart your server when you modify this file.
276
+
277
+ InvisibleCaptcha.setup do |config|
278
+ config.timestamp_enabled = !Rails.env.test?
279
+ end
280
+ ```
281
+
282
+ Another option is to wait for the timestamp check to be valid:
283
+
284
+ ```ruby
285
+ # Maybe in a before block
286
+ InvisibleCaptcha.init!
287
+ InvisibleCaptcha.timestamp_threshold = 1
288
+
289
+ # Before testing your controller action
290
+ sleep InvisibleCaptcha.timestamp_threshold
291
+ ```
292
+
161
293
  ## Contribute
162
294
 
163
295
  Any kind of idea, feedback or bug report are welcome! Open an [issue](https://github.com/markets/invisible_captcha/issues) or send a [pull request](https://github.com/markets/invisible_captcha/pulls).
@@ -179,6 +311,12 @@ $ bundle exec appraisal install
179
311
  $ bundle exec appraisal rspec
180
312
  ```
181
313
 
314
+ Run specs against specific version:
315
+
316
+ ```
317
+ $ bundle exec appraisal rails-6.0 rspec
318
+ ```
319
+
182
320
  ### Demo
183
321
 
184
322
  Start a sample Rails app ([source code](spec/dummy)) with `InvisibleCaptcha` integrated:
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "bundler/gem_tasks"
2
4
  require 'rspec/core/rake_task'
3
5
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "rails", "~> 3.2.0"
5
+ gem "rails", "~> 5.2.0"
6
6
 
7
- gemspec :path => "../"
7
+ gemspec path: "../"
@@ -2,6 +2,6 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "rails", "~> 4.1.0"
5
+ gem "rails", "~> 6.0.0"
6
6
 
7
- gemspec :path => "../"
7
+ gemspec path: "../"
@@ -2,6 +2,6 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "rails", "~> 5.1.0"
5
+ gem "rails", "~> 6.1.0"
6
6
 
7
- gemspec :path => "../"
7
+ gemspec path: "../"
@@ -5,8 +5,8 @@ Gem::Specification.new do |spec|
5
5
  spec.version = InvisibleCaptcha::VERSION
6
6
  spec.authors = ["Marc Anguera Insa"]
7
7
  spec.email = ["srmarc.ai@gmail.com"]
8
- spec.description = "Unobtrusive, flexible and simple spam protection for Rails applications using honeypot strategy for better user experience."
9
- spec.summary = "Simple honeypot protection for RoR apps"
8
+ spec.description = "Unobtrusive, flexible and complete spam protection for Rails applications using honeypot strategy for better user experience."
9
+ spec.summary = "Honeypot spam protection for Rails"
10
10
  spec.homepage = "https://github.com/markets/invisible_captcha"
11
11
  spec.license = "MIT"
12
12
 
@@ -15,11 +15,8 @@ Gem::Specification.new do |spec|
15
15
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
16
16
  spec.require_paths = ["lib"]
17
17
 
18
- spec.add_dependency 'rails', '>= 3.2.0'
18
+ spec.add_dependency 'rails', '>= 5.0'
19
19
 
20
- spec.add_development_dependency 'rspec-rails', '~> 3.1'
20
+ spec.add_development_dependency 'rspec-rails'
21
21
  spec.add_development_dependency 'appraisal'
22
- spec.add_development_dependency 'test-unit', '~> 3.0'
23
- spec.add_development_dependency 'byebug'
24
22
  end
25
-