invisible_captcha 2.0.0 → 2.3.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: 86acfe71e903568702c63c261bfd32a066dbc947d1c2fa2b4956178a7de591db
4
- data.tar.gz: a9a8768c5bcfb9a7656e0638dde4bf40e169719385bf947dace7fa4d34f03656
3
+ metadata.gz: 8421f23e2c4ca4f1512efe6f0ac5b7d538c21fae25c94dae1eb12080bcc7bff2
4
+ data.tar.gz: d80bbba59b964e84f677620a2c24a5047744a95733805981583034e3629034a4
5
5
  SHA512:
6
- metadata.gz: ec1ed1b9f7bef2e753f7b736dbce8124b5cd4c699e4075cbd15fdade99e6f332025e3088f0754315b18f8bde48b7e883c3470f840a535a3631f2e5c659c415df
7
- data.tar.gz: 3698ed54f31c8f87730dcd92e14c2076e10aaa98a14a765e7d3be8bfd0e259385572b5d3252b8328fb4a539634bb8e086270b2e7e34117ab76a15ab4f42c095b
6
+ metadata.gz: 94f28d33d89e5dcfa741d97c34c4a42645a5d3f668238653341cd402c1e1e3bfbf8d2c42f5bc6d9d8bda3815d3e7036c0dae13965691e7409ce5df1ff5763bee
7
+ data.tar.gz: 2a187357840766a3eae1c40f032015c38f07a2b12955c9bfc82f95251b81138a6eab8e69ccd0adbfb9e5f13c095a541e869f2b3d5672e29a75b893253702965e
@@ -0,0 +1,35 @@
1
+ name: CI
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ test:
7
+ name: CI
8
+ runs-on: ubuntu-latest
9
+ env:
10
+ BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ ruby: ["2.7", "3.0", "3.1", "3.2"]
15
+ gemfile: [rails_6.0, rails_6.1, rails_7.0, rails_7.1]
16
+ exclude:
17
+ - ruby: "3.1"
18
+ gemfile: rails_6.0
19
+ - ruby: "3.2"
20
+ gemfile: rails_6.0
21
+ include:
22
+ - ruby: "2.7"
23
+ gemfile: rails_5.2
24
+ - ruby: head
25
+ gemfile: rails_7.0
26
+ steps:
27
+ - uses: actions/checkout@v3
28
+ - uses: ruby/setup-ruby@v1
29
+ with:
30
+ ruby-version: ${{ matrix.ruby }}
31
+ bundler-cache: true
32
+ - run: bundle exec rspec
33
+ continue-on-error: ${{ endsWith(matrix.ruby, 'head') }}
34
+ - name: Upload coverage reports to Codecov
35
+ uses: codecov/codecov-action@v3
data/Appraisals CHANGED
@@ -1,11 +1,11 @@
1
- appraise "rails-6.1" do
2
- gem "rails", "~> 6.1.0"
3
- end
4
-
5
- appraise "rails-6.0" do
6
- gem "rails", "~> 6.0.0"
7
- end
8
-
9
- appraise "rails-5.2" do
10
- gem "rails", "~> 5.2.0"
1
+ %w(
2
+ 7.1
3
+ 7.0
4
+ 6.1
5
+ 6.0
6
+ 5.2
7
+ ).each do |version|
8
+ appraise "rails-#{version}" do
9
+ gem "rails", "~> #{version}.0"
10
+ end
11
11
  end
data/CHANGELOG.md CHANGED
@@ -2,6 +2,22 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [2.3.0]
6
+
7
+ - Run honeypot + spinner checks and their callback also if timestamp triggers but passes through (#132)
8
+ - Mark as spam requests with no spinner value (#134)
9
+
10
+ ## [2.2.0]
11
+
12
+ - Official support for Rails 7.1
13
+ - Fix flash message for `on_timestamp_spam` callback (#125)
14
+ - Fix potential error when lookup the honeypot parameter using (#128)
15
+
16
+ ## [2.1.0]
17
+
18
+ - Drop official support for EOL Rubies: 2.5 and 2.6
19
+ - Allow random honeypots to be scoped (#117)
20
+
5
21
  ## [2.0.0]
6
22
 
7
23
  - New spinner, IP based, validation check (#89)
@@ -125,6 +141,9 @@ All notable changes to this project will be documented in this file.
125
141
 
126
142
  - First version of controller filters
127
143
 
144
+ [2.3.0]: https://github.com/markets/invisible_captcha/compare/v2.2.0...v2.3.0
145
+ [2.2.0]: https://github.com/markets/invisible_captcha/compare/v2.1.0...v2.2.0
146
+ [2.1.0]: https://github.com/markets/invisible_captcha/compare/v2.0.0...v2.1.0
128
147
  [2.0.0]: https://github.com/markets/invisible_captcha/compare/v1.1.0...v2.0.0
129
148
  [1.1.0]: https://github.com/markets/invisible_captcha/compare/v1.0.1...v1.1.0
130
149
  [1.0.1]: https://github.com/markets/invisible_captcha/compare/v1.0.0...v1.0.1
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2012-2021 Marc Anguera Insa
1
+ Copyright 2012-2024 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,7 +1,8 @@
1
1
  # Invisible Captcha
2
2
 
3
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
+ [![Build Status](https://github.com/markets/invisible_captcha/workflows/CI/badge.svg)](https://github.com/markets/invisible_captcha/actions)
5
+ [![codecov](https://codecov.io/gh/markets/invisible_captcha/branch/master/graph/badge.svg?token=nADSa6rbhM)](https://codecov.io/gh/markets/invisible_captcha)
5
6
 
6
7
  > Complete and flexible spam protection solution for Rails applications.
7
8
 
@@ -21,7 +22,7 @@ It also comes with:
21
22
 
22
23
  ## Installation
23
24
 
24
- Invisible Captcha is tested against Rails `>= 5.2` and Ruby `>= 2.5`.
25
+ Invisible Captcha is tested against Rails `>= 5.2` and Ruby `>= 2.7`.
25
26
 
26
27
  Add this line to your Gemfile and then execute `bundle install`:
27
28
 
@@ -63,6 +64,8 @@ class TopicsController < ApplicationController
63
64
  end
64
65
  ```
65
66
 
67
+ You should _not_ name your method `on_spam`, as this will collide with an internal method of the same name.
68
+
66
69
  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:
67
70
 
68
71
  ```erb
@@ -97,6 +100,8 @@ invisible_captcha only: [:new_contact]
97
100
 
98
101
  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
102
 
103
+ **NOTE:** This gem relies on data set by the backend, so in order to properly work, your forms should be rendered by Rails. Forms generated via JavaScript are not going to work well.
104
+
100
105
  ## Options and customization
101
106
 
102
107
  This section contains a description of all plugin options and customizations.
@@ -106,13 +111,13 @@ This section contains a description of all plugin options and customizations.
106
111
  You can customize:
107
112
 
108
113
  - `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).
114
+ - `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)). Beware that Chrome may ignore `autocomplete="off"`. Thus, consider not to use field names, which would be autocompleted, like for example `name`, `country`.
110
115
  - `visual_honeypots`: make honeypots visible, also useful to test/debug your implementation.
111
116
  - `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
117
  - `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
118
  - `timestamp_error_message`: flash error message thrown when form submitted quicker than the `timestamp_threshold` value. It uses I18n by default.
114
119
  - `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.
120
+ - `spinner_enabled`: option to disable the IP spinner validation. By default, true.
116
121
  - `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.
117
122
 
118
123
  To change these defaults, add the following to an initializer (recommended `config/initializers/invisible_captcha.rb`):
@@ -206,7 +211,7 @@ The `data` passed to the subscriber is hash containing information about the req
206
211
 
207
212
  ```ruby
208
213
  {
209
- message: "Invisible Captcha honeypot param 'subtitle' was present.",
214
+ message: "Honeypot param 'subtitle' was present.",
210
215
  remote_ip: '127.0.0.1',
211
216
  user_agent: 'Chrome 77',
212
217
  controller: 'users',
@@ -220,7 +225,7 @@ The `data` passed to the subscriber is hash containing information about the req
220
225
  }
221
226
  ```
222
227
 
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._
228
+ **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
229
 
225
230
  ### Content Security Policy
226
231
 
@@ -247,7 +252,7 @@ And in your view helper, you need to pass `nonce: true` to the `invisible_captch
247
252
  <%= invisible_captcha nonce: true %>
248
253
  ```
249
254
 
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:
255
+ **NOTE:** 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
256
 
252
257
  * https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
253
258
  * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
@@ -282,7 +287,7 @@ end
282
287
  Another option is to wait for the timestamp check to be valid:
283
288
 
284
289
  ```ruby
285
- # Maybe in a before block
290
+ # Maybe inside a before block
286
291
  InvisibleCaptcha.init!
287
292
  InvisibleCaptcha.timestamp_threshold = 1
288
293
 
@@ -290,6 +295,26 @@ InvisibleCaptcha.timestamp_threshold = 1
290
295
  sleep InvisibleCaptcha.timestamp_threshold
291
296
  ```
292
297
 
298
+ If you're using the "random honeypot" approach, you may want to set a known honeypot:
299
+
300
+ ```ruby
301
+ config.honeypots = ['my_honeypot_field'] if Rails.env.test?
302
+ ```
303
+
304
+ And for the "spinner validation" check, you may want to disable it:
305
+
306
+ ```ruby
307
+ config.spinner_enabled = !Rails.env.test?
308
+ ```
309
+
310
+ Or alternativelly, you should send a valid spinner value along your requests:
311
+
312
+ ```ruby
313
+ # RSpec example
314
+ session[:invisible_captcha_spinner] = '32ab649161f9f6faeeb323746de1a25d'
315
+ post :create, params: { topic: { title: 'foo' }, spinner: '32ab649161f9f6faeeb323746de1a25d' }
316
+ ```
317
+
293
318
  ## Contribute
294
319
 
295
320
  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).
data/Rakefile CHANGED
@@ -1,11 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "bundler/gem_tasks"
4
- require 'rspec/core/rake_task'
5
-
6
- RSpec::Core::RakeTask.new(:spec)
7
-
8
- task :default => :spec
9
4
 
10
5
  desc 'Start development Rails app'
11
6
  task :web do
@@ -16,4 +11,4 @@ task :web do
16
11
 
17
12
  Dir.chdir(app_path)
18
13
  exec("rails s -p #{port}")
19
- end
14
+ end
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~> 7.0.0"
6
+
7
+ gemspec path: "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~> 7.1.0"
6
+
7
+ gemspec path: "../"
@@ -15,8 +15,11 @@ 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', '>= 5.0'
18
+ spec.add_dependency 'rails', '>= 5.2'
19
19
 
20
20
  spec.add_development_dependency 'rspec-rails'
21
21
  spec.add_development_dependency 'appraisal'
22
+ spec.add_development_dependency 'webrick'
23
+ spec.add_development_dependency 'simplecov'
24
+ spec.add_development_dependency 'simplecov-cobertura'
22
25
  end
@@ -21,7 +21,10 @@ module InvisibleCaptcha
21
21
  def detect_spam(options = {})
22
22
  if timestamp_spam?(options)
23
23
  on_timestamp_spam(options)
24
- elsif honeypot_spam?(options) || spinner_spam?
24
+ return if performed?
25
+ end
26
+
27
+ if honeypot_spam?(options) || spinner_spam?
25
28
  on_spam(options)
26
29
  end
27
30
  end
@@ -30,7 +33,8 @@ module InvisibleCaptcha
30
33
  if action = options[:on_timestamp_spam]
31
34
  send(action)
32
35
  else
33
- redirect_back(fallback_location: root_path, flash: { error: InvisibleCaptcha.timestamp_error_message })
36
+ flash[:error] = InvisibleCaptcha.timestamp_error_message
37
+ redirect_back(fallback_location: root_path)
34
38
  end
35
39
  end
36
40
 
@@ -55,7 +59,7 @@ module InvisibleCaptcha
55
59
 
56
60
  # Consider as spam if timestamp not in session, cause that means the form was not fetched at all
57
61
  unless timestamp
58
- warn_spam("Invisible Captcha timestamp not found in session.")
62
+ warn_spam("Timestamp not found in session.")
59
63
  return true
60
64
  end
61
65
 
@@ -64,7 +68,7 @@ module InvisibleCaptcha
64
68
 
65
69
  # Consider as spam if form submitted too quickly
66
70
  if time_to_submit < threshold
67
- warn_spam("Invisible Captcha timestamp threshold not reached (took #{time_to_submit.to_i}s).")
71
+ warn_spam("Timestamp threshold not reached (took #{time_to_submit.to_i}s).")
68
72
  return true
69
73
  end
70
74
 
@@ -72,8 +76,8 @@ module InvisibleCaptcha
72
76
  end
73
77
 
74
78
  def spinner_spam?
75
- if InvisibleCaptcha.spinner_enabled && params[:spinner] != session[:invisible_captcha_spinner]
76
- warn_spam("Invisible Captcha spinner value mismatch")
79
+ if InvisibleCaptcha.spinner_enabled && (params[:spinner].blank? || params[:spinner] != session[:invisible_captcha_spinner])
80
+ warn_spam("Spinner value mismatch")
77
81
  return true
78
82
  end
79
83
 
@@ -88,8 +92,8 @@ module InvisibleCaptcha
88
92
  # If honeypot is defined for this controller-action, search for:
89
93
  # - honeypot: params[:subtitle]
90
94
  # - honeypot with scope: params[:topic][:subtitle]
91
- if params[honeypot].present? || params.dig(scope, honeypot).present?
92
- warn_spam("Invisible Captcha honeypot param '#{honeypot}' was present.")
95
+ if params[honeypot].present? || (params[scope] && params[scope][honeypot].present?)
96
+ warn_spam("Honeypot param '#{honeypot}' was present.")
93
97
  return true
94
98
  else
95
99
  # No honeypot spam detected, remove honeypot from params to avoid UnpermittedParameters exceptions
@@ -98,8 +102,8 @@ module InvisibleCaptcha
98
102
  end
99
103
  else
100
104
  InvisibleCaptcha.honeypots.each do |default_honeypot|
101
- if params[default_honeypot].present?
102
- warn_spam("Invisible Captcha honeypot param '#{default_honeypot}' was present.")
105
+ if params[default_honeypot].present? || (params[scope] && params[scope][default_honeypot].present?)
106
+ warn_spam("Honeypot param '#{scope}.#{default_honeypot}' was present.")
103
107
  return true
104
108
  end
105
109
  end
@@ -109,7 +113,9 @@ module InvisibleCaptcha
109
113
  end
110
114
 
111
115
  def warn_spam(message)
112
- logger.warn("Potential spam detected for IP #{request.remote_ip}. #{message}")
116
+ message = "[Invisible Captcha] Potential spam detected for IP #{request.remote_ip}. #{message}"
117
+
118
+ logger.warn(message)
113
119
 
114
120
  ActiveSupport::Notifications.instrument(
115
121
  'invisible_captcha.spam_detected',
@@ -2,7 +2,7 @@
2
2
 
3
3
  module InvisibleCaptcha
4
4
  module FormHelpers
5
- def invisible_captcha(honeypot, options = {})
5
+ def invisible_captcha(honeypot = nil, options = {})
6
6
  @template.invisible_captcha(honeypot, self.object_name, options)
7
7
  end
8
8
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module InvisibleCaptcha
4
- VERSION = "2.0.0"
4
+ VERSION = "2.3.0"
5
5
  end
@@ -57,7 +57,7 @@ module InvisibleCaptcha
57
57
  concat label_tag(build_label_name(honeypot, scope), label)
58
58
  concat text_field_tag(build_input_name(honeypot, scope), nil, default_honeypot_options.merge(options))
59
59
  if InvisibleCaptcha.spinner_enabled
60
- concat hidden_field_tag("spinner", session[:invisible_captcha_spinner])
60
+ concat hidden_field_tag("spinner", session[:invisible_captcha_spinner], id: nil)
61
61
  end
62
62
  end
63
63
  end
@@ -71,6 +71,12 @@ RSpec.describe InvisibleCaptcha::ControllerExt, type: :controller do
71
71
  .to be_present
72
72
  end
73
73
 
74
+ it 'runs on_spam callback if on_timestamp_spam callback is defined but passes' do
75
+ put :test_passthrough, params: { id: 1, topic: { title: 'bar', subtitle: 'foo' } }
76
+
77
+ expect(response.status).to eq(204)
78
+ end
79
+
74
80
  context 'successful submissions' do
75
81
  it 'passes if submission on or after timestamp_threshold' do
76
82
  sleep InvisibleCaptcha.timestamp_threshold
@@ -84,7 +90,7 @@ RSpec.describe InvisibleCaptcha::ControllerExt, type: :controller do
84
90
  }
85
91
 
86
92
  expect(flash[:error]).not_to be_present
87
- expect(response.body).to be_present
93
+ expect(response.body).to redirect_to(new_topic_path)
88
94
 
89
95
  # Make sure session is cleared
90
96
  expect(session[:invisible_captcha_timestamp]).to be_nil
@@ -96,7 +102,13 @@ RSpec.describe InvisibleCaptcha::ControllerExt, type: :controller do
96
102
  post :publish, params: { id: 1 }
97
103
 
98
104
  expect(flash[:error]).not_to be_present
99
- expect(response.body).to be_present
105
+ expect(response.body).to redirect_to(new_topic_path)
106
+ end
107
+
108
+ it 'passes if on_timestamp_spam doesn\'t perform' do
109
+ put :test_passthrough, params: { id: 1, topic: { title: 'bar' } }
110
+
111
+ expect(response.body).to redirect_to(new_topic_path)
100
112
  end
101
113
  end
102
114
  end
@@ -121,6 +133,50 @@ RSpec.describe InvisibleCaptcha::ControllerExt, type: :controller do
121
133
  expect(response.body).to be_present
122
134
  end
123
135
 
136
+ context 'with random honeypot' do
137
+ context 'auto-scoped' do
138
+ it 'passes with no spam' do
139
+ post :categorize, params: { topic: { title: 'foo' } }
140
+
141
+ expect(response.body).to redirect_to(new_topic_path)
142
+ end
143
+
144
+ it 'fails with spam' do
145
+ post :categorize, params: { topic: { "#{InvisibleCaptcha.honeypots.sample}": 'foo' } }
146
+
147
+ expect(response.body).not_to redirect_to(new_topic_path)
148
+ end
149
+ end
150
+
151
+ context 'with no scope' do
152
+ it 'passes with no spam' do
153
+ post :categorize
154
+
155
+ expect(response.body).to redirect_to(new_topic_path)
156
+ end
157
+
158
+ it 'fails with spam' do
159
+ post :categorize, params: { "#{InvisibleCaptcha.honeypots.sample}": 'foo' }
160
+
161
+ expect(response.body).not_to redirect_to(new_topic_path)
162
+ end
163
+ end
164
+
165
+ context 'with scope' do
166
+ it 'fails with spam' do
167
+ post :rename, params: { topic: { "#{InvisibleCaptcha.honeypots.sample}": 'foo' } }
168
+
169
+ expect(response.body).to be_blank
170
+ end
171
+
172
+ it 'passes with no spam' do
173
+ post :rename, params: { topic: { title: 'foo' } }
174
+
175
+ expect(response.body).to be_blank
176
+ end
177
+ end
178
+ end
179
+
124
180
  it 'allow a custom on_spam callback' do
125
181
  put :update, params: { id: 1, topic: { subtitle: 'foo' } }
126
182
 
@@ -148,8 +204,8 @@ RSpec.describe InvisibleCaptcha::ControllerExt, type: :controller do
148
204
  after { ActiveSupport::Notifications.unsubscribe(subscriber) }
149
205
 
150
206
  it 'dispatches an `invisible_captcha.spam_detected` event' do
151
- expect(dummy_handler).to receive(:handle_event).once.with(
152
- message: "Invisible Captcha honeypot param 'subtitle' was present.",
207
+ expect(dummy_handler).to receive(:handle_event).once.with({
208
+ message: "[Invisible Captcha] Potential spam detected for IP 0.0.0.0. Honeypot param 'subtitle' was present.",
153
209
  remote_ip: '0.0.0.0',
154
210
  user_agent: 'Rails Testing',
155
211
  controller: 'topics',
@@ -160,7 +216,7 @@ RSpec.describe InvisibleCaptcha::ControllerExt, type: :controller do
160
216
  controller: 'topics',
161
217
  action: 'create'
162
218
  }
163
- )
219
+ })
164
220
 
165
221
  post :create, params: { topic: { subtitle: 'foo' } }
166
222
  end
@@ -9,6 +9,14 @@ class TopicsController < ApplicationController
9
9
 
10
10
  invisible_captcha honeypot: :subtitle, only: :copy, timestamp_enabled: false
11
11
 
12
+ invisible_captcha scope: :topic, only: :rename
13
+
14
+ invisible_captcha only: :categorize
15
+
16
+ invisible_captcha honeypot: :subtitle, only: :test_passthrough,
17
+ on_spam: :catching_on_spam_callback,
18
+ on_timestamp_spam: :on_timestamp_spam_callback_with_passthrough
19
+
12
20
  def index
13
21
  redirect_to new_topic_path
14
22
  end
@@ -28,6 +36,14 @@ class TopicsController < ApplicationController
28
36
  end
29
37
 
30
38
  def update
39
+ redirect_to new_topic_path
40
+ end
41
+
42
+ def rename
43
+ end
44
+
45
+ def categorize
46
+ redirect_to new_topic_path
31
47
  end
32
48
 
33
49
  def publish
@@ -44,6 +60,10 @@ class TopicsController < ApplicationController
44
60
  end
45
61
  end
46
62
 
63
+ def test_passthrough
64
+ redirect_to new_topic_path
65
+ end
66
+
47
67
  private
48
68
 
49
69
  def custom_callback
@@ -53,4 +73,12 @@ class TopicsController < ApplicationController
53
73
  def custom_timestamp_callback
54
74
  head(204)
55
75
  end
76
+
77
+ def on_timestamp_spam_callback_with_passthrough
78
+ end
79
+
80
+ def catching_on_spam_callback
81
+ head(204)
82
+ end
83
+
56
84
  end
@@ -1,9 +1,8 @@
1
1
  <!DOCTYPE html>
2
2
  <html>
3
3
  <head>
4
- <title>Dummy</title>
5
- <%= stylesheet_link_tag "application", :media => "all" %>
6
- <%= javascript_include_tag "application" %>
4
+ <title>InvisibleCaptcha Demo</title>
5
+ <%= stylesheet_link_tag "/styles.css" %>
7
6
  <%= csrf_meta_tags %>
8
7
  <%= invisible_captcha_styles %>
9
8
  </head>
@@ -25,5 +24,11 @@
25
24
  <% end %>
26
25
 
27
26
  <%= yield %>
27
+
28
+ <footer>
29
+ Running on
30
+ <b>Ruby <%= RUBY_VERSION %></b> and
31
+ <b>Rails <%= Rails.version %></b>
32
+ </footer>
28
33
  </body>
29
34
  </html>
@@ -2,9 +2,7 @@ require File.expand_path('../boot', __FILE__)
2
2
 
3
3
  require 'action_controller/railtie'
4
4
  require 'action_view/railtie'
5
- require 'action_mailer/railtie'
6
5
  require 'active_model/railtie'
7
- require 'sprockets/railtie'
8
6
 
9
7
  # Require the gems listed in Gemfile, including any gems
10
8
  # you've limited to :test, :development, or :production.
@@ -14,7 +14,7 @@ Dummy::Application.configure do
14
14
  config.action_controller.perform_caching = false
15
15
 
16
16
  # Don't care if the mailer can't send.
17
- config.action_mailer.raise_delivery_errors = false
17
+ # config.action_mailer.raise_delivery_errors = false
18
18
 
19
19
  # Print deprecation notices to the Rails logger.
20
20
  config.active_support.deprecation = :log
@@ -31,9 +31,6 @@ Dummy::Application.configure do
31
31
  # yet still be able to expire them through the digest params.
32
32
  # config.assets.digest = true
33
33
 
34
- # quiet assets
35
- config.assets.quiet = true
36
-
37
34
  # Adds additional error checking when serving assets at runtime.
38
35
  # Checks for improperly declared sprockets dependencies.
39
36
  # Raises helpful error messages.
@@ -30,7 +30,7 @@ Dummy::Application.configure do
30
30
  # Tell Action Mailer not to deliver emails to the real world.
31
31
  # The :test delivery method accumulates sent emails in the
32
32
  # ActionMailer::Base.deliveries array.
33
- config.action_mailer.delivery_method = :test
33
+ # config.action_mailer.delivery_method = :test
34
34
 
35
35
  # Print deprecation notices to the stderr.
36
36
  config.active_support.deprecation = :stderr
@@ -1,7 +1,10 @@
1
1
  Rails.application.routes.draw do
2
2
  resources :topics do
3
3
  post :publish, on: :member
4
+ post :rename, on: :collection
5
+ post :categorize, on: :collection
4
6
  post :copy, on: :collection
7
+ post :test_passthrough, on: :collection
5
8
  end
6
9
 
7
10
  root to: 'topics#new'
@@ -8,8 +8,11 @@ h1 {
8
8
  border-bottom: 3px solid;
9
9
  }
10
10
 
11
- input,
12
- textarea {
11
+ a {
12
+ color: #000;
13
+ }
14
+
15
+ input, textarea {
13
16
  border: 0;
14
17
  margin-bottom: 1.5em;
15
18
  }
@@ -19,13 +22,24 @@ input {
19
22
  }
20
23
 
21
24
  button {
22
- background-color: #f0f0f0;
25
+ color: #fff;
26
+ background-color: #000;
27
+ border: none;
23
28
  border-radius: 0.25em;
24
29
  height: 3em;
25
30
  width: 10em;
26
31
  font-size: 1em;
27
32
  }
28
33
 
34
+ footer {
35
+ position: fixed;
36
+ bottom: 0;
37
+ width: 100%;
38
+ padding: 1em 0;
39
+ font-size: 0.8em;
40
+ background-color: #ccc;
41
+ }
42
+
29
43
  .errors {
30
44
  color: darkred;
31
- }
45
+ }
data/spec/spec_helper.rb CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  ENV['RAILS_ENV'] = 'test'
4
4
 
5
+ require 'simplecov'
6
+ if ENV['CI']
7
+ require 'simplecov-cobertura'
8
+ SimpleCov.formatter = SimpleCov::Formatter::CoberturaFormatter
9
+ end
10
+ SimpleCov.start
11
+
5
12
  require File.expand_path("../dummy/config/environment.rb", __FILE__)
6
13
  require 'rspec/rails'
7
14
  require 'invisible_captcha'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: invisible_captcha
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marc Anguera Insa
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-02-27 00:00:00.000000000 Z
11
+ date: 2024-03-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '5.0'
19
+ version: '5.2'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '5.0'
26
+ version: '5.2'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rspec-rails
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -52,6 +52,48 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: webrick
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: simplecov
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: simplecov-cobertura
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
55
97
  description: Unobtrusive, flexible and complete spam protection for Rails applications
56
98
  using honeypot strategy for better user experience.
57
99
  email:
@@ -60,9 +102,9 @@ executables: []
60
102
  extensions: []
61
103
  extra_rdoc_files: []
62
104
  files:
105
+ - ".github/workflows/ci.yml"
63
106
  - ".gitignore"
64
107
  - ".rspec"
65
- - ".travis.yml"
66
108
  - Appraisals
67
109
  - CHANGELOG.md
68
110
  - Gemfile
@@ -72,6 +114,8 @@ files:
72
114
  - gemfiles/rails_5.2.gemfile
73
115
  - gemfiles/rails_6.0.gemfile
74
116
  - gemfiles/rails_6.1.gemfile
117
+ - gemfiles/rails_7.0.gemfile
118
+ - gemfiles/rails_7.1.gemfile
75
119
  - invisible_captcha.gemspec
76
120
  - lib/invisible_captcha.rb
77
121
  - lib/invisible_captcha/controller_ext.rb
@@ -82,13 +126,9 @@ files:
82
126
  - spec/controllers_spec.rb
83
127
  - spec/dummy/README.md
84
128
  - spec/dummy/Rakefile
85
- - spec/dummy/app/assets/config/manifest.js
86
- - spec/dummy/app/assets/javascripts/application.js
87
- - spec/dummy/app/assets/stylesheets/application.css
88
129
  - spec/dummy/app/controllers/application_controller.rb
89
130
  - spec/dummy/app/controllers/topics_controller.rb
90
131
  - spec/dummy/app/helpers/application_helper.rb
91
- - spec/dummy/app/mailers/.gitkeep
92
132
  - spec/dummy/app/models/topic.rb
93
133
  - spec/dummy/app/views/layouts/application.html.erb
94
134
  - spec/dummy/app/views/topics/new.html.erb
@@ -101,7 +141,6 @@ files:
101
141
  - spec/dummy/config/boot.rb
102
142
  - spec/dummy/config/environment.rb
103
143
  - spec/dummy/config/environments/development.rb
104
- - spec/dummy/config/environments/production.rb
105
144
  - spec/dummy/config/environments/test.rb
106
145
  - spec/dummy/config/initializers/backtrace_silencers.rb
107
146
  - spec/dummy/config/initializers/cookies_serializer.rb
@@ -114,12 +153,12 @@ files:
114
153
  - spec/dummy/config/locales/en.yml
115
154
  - spec/dummy/config/routes.rb
116
155
  - spec/dummy/config/secrets.yml
117
- - spec/dummy/lib/assets/.gitkeep
118
156
  - spec/dummy/log/.gitkeep
119
157
  - spec/dummy/public/404.html
120
158
  - spec/dummy/public/422.html
121
159
  - spec/dummy/public/500.html
122
160
  - spec/dummy/public/favicon.ico
161
+ - spec/dummy/public/styles.css
123
162
  - spec/invisible_captcha_spec.rb
124
163
  - spec/spec_helper.rb
125
164
  - spec/view_helpers_spec.rb
@@ -127,7 +166,7 @@ homepage: https://github.com/markets/invisible_captcha
127
166
  licenses:
128
167
  - MIT
129
168
  metadata: {}
130
- post_install_message:
169
+ post_install_message:
131
170
  rdoc_options: []
132
171
  require_paths:
133
172
  - lib
@@ -142,21 +181,17 @@ required_rubygems_version: !ruby/object:Gem::Requirement
142
181
  - !ruby/object:Gem::Version
143
182
  version: '0'
144
183
  requirements: []
145
- rubygems_version: 3.0.3
146
- signing_key:
184
+ rubygems_version: 3.4.10
185
+ signing_key:
147
186
  specification_version: 4
148
187
  summary: Honeypot spam protection for Rails
149
188
  test_files:
150
189
  - spec/controllers_spec.rb
151
190
  - spec/dummy/README.md
152
191
  - spec/dummy/Rakefile
153
- - spec/dummy/app/assets/config/manifest.js
154
- - spec/dummy/app/assets/javascripts/application.js
155
- - spec/dummy/app/assets/stylesheets/application.css
156
192
  - spec/dummy/app/controllers/application_controller.rb
157
193
  - spec/dummy/app/controllers/topics_controller.rb
158
194
  - spec/dummy/app/helpers/application_helper.rb
159
- - spec/dummy/app/mailers/.gitkeep
160
195
  - spec/dummy/app/models/topic.rb
161
196
  - spec/dummy/app/views/layouts/application.html.erb
162
197
  - spec/dummy/app/views/topics/new.html.erb
@@ -169,7 +204,6 @@ test_files:
169
204
  - spec/dummy/config/boot.rb
170
205
  - spec/dummy/config/environment.rb
171
206
  - spec/dummy/config/environments/development.rb
172
- - spec/dummy/config/environments/production.rb
173
207
  - spec/dummy/config/environments/test.rb
174
208
  - spec/dummy/config/initializers/backtrace_silencers.rb
175
209
  - spec/dummy/config/initializers/cookies_serializer.rb
@@ -182,12 +216,12 @@ test_files:
182
216
  - spec/dummy/config/locales/en.yml
183
217
  - spec/dummy/config/routes.rb
184
218
  - spec/dummy/config/secrets.yml
185
- - spec/dummy/lib/assets/.gitkeep
186
219
  - spec/dummy/log/.gitkeep
187
220
  - spec/dummy/public/404.html
188
221
  - spec/dummy/public/422.html
189
222
  - spec/dummy/public/500.html
190
223
  - spec/dummy/public/favicon.ico
224
+ - spec/dummy/public/styles.css
191
225
  - spec/invisible_captcha_spec.rb
192
226
  - spec/spec_helper.rb
193
227
  - spec/view_helpers_spec.rb
data/.travis.yml DELETED
@@ -1,18 +0,0 @@
1
- language: ruby
2
- cache: bundler
3
- rvm:
4
- - ruby-head
5
- - 3.0.0
6
- - 2.7.2
7
- - 2.6.5
8
- - 2.5.8
9
- gemfile:
10
- - gemfiles/rails_6.1.gemfile
11
- - gemfiles/rails_6.0.gemfile
12
- - gemfiles/rails_5.2.gemfile
13
- matrix:
14
- exclude:
15
- - rvm: 3.0.0
16
- gemfile: gemfiles/rails_5.2.gemfile
17
- allow_failures:
18
- - rvm: ruby-head
@@ -1,2 +0,0 @@
1
- //= link_directory ../javascripts .js
2
- //= link_directory ../stylesheets .css
@@ -1 +0,0 @@
1
- console.log('Hi from Invisible Captcha!');
File without changes
@@ -1,86 +0,0 @@
1
- Rails.application.configure do
2
- # Settings specified here will take precedence over those in config/application.rb.
3
-
4
- # Code is not reloaded between requests.
5
- config.cache_classes = true
6
-
7
- # Eager load code on boot. This eager loads most of Rails and
8
- # your application in memory, allowing both threaded web servers
9
- # and those relying on copy on write to perform better.
10
- # Rake tasks automatically ignore this option for performance.
11
- config.eager_load = true
12
-
13
- # Full error reports are disabled and caching is turned on.
14
- config.consider_all_requests_local = false
15
- config.action_controller.perform_caching = true
16
-
17
- # Disable serving static files from the `/public` folder by default since
18
- # Apache or NGINX already handles this.
19
- config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
20
-
21
- # Compress JavaScripts and CSS.
22
- config.assets.js_compressor = :uglifier
23
- # config.assets.css_compressor = :sass
24
-
25
- # Do not fallback to assets pipeline if a precompiled asset is missed.
26
- config.assets.compile = false
27
-
28
- # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb
29
-
30
- # Enable serving of images, stylesheets, and JavaScripts from an asset server.
31
- # config.action_controller.asset_host = 'http://assets.example.com'
32
-
33
- # Specifies the header that your server uses for sending files.
34
- # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
35
- # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
36
-
37
- # Mount Action Cable outside main process or domain
38
- # config.action_cable.mount_path = nil
39
- # config.action_cable.url = 'wss://example.com/cable'
40
- # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ]
41
-
42
- # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
43
- # config.force_ssl = true
44
-
45
- # Use the lowest log level to ensure availability of diagnostic information
46
- # when problems arise.
47
- config.log_level = :debug
48
-
49
- # Prepend all log lines with the following tags.
50
- config.log_tags = [ :request_id ]
51
-
52
- # Use a different cache store in production.
53
- # config.cache_store = :mem_cache_store
54
-
55
- # Use a real queuing backend for Active Job (and separate queues per environment)
56
- # config.active_job.queue_adapter = :resque
57
- # config.active_job.queue_name_prefix = "dummy_#{Rails.env}"
58
- config.action_mailer.perform_caching = false
59
-
60
- # Ignore bad email addresses and do not raise email delivery errors.
61
- # Set this to true and configure the email server for immediate delivery to raise delivery errors.
62
- # config.action_mailer.raise_delivery_errors = false
63
-
64
- # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
65
- # the I18n.default_locale when a translation cannot be found).
66
- config.i18n.fallbacks = true
67
-
68
- # Send deprecation notices to registered listeners.
69
- config.active_support.deprecation = :notify
70
-
71
- # Use default logging formatter so that PID and timestamp are not suppressed.
72
- config.log_formatter = ::Logger::Formatter.new
73
-
74
- # Use a different logger for distributed setups.
75
- # require 'syslog/logger'
76
- # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
77
-
78
- if ENV["RAILS_LOG_TO_STDOUT"].present?
79
- logger = ActiveSupport::Logger.new(STDOUT)
80
- logger.formatter = config.log_formatter
81
- config.logger = ActiveSupport::TaggedLogging.new(logger)
82
- end
83
-
84
- # Do not dump schema after migrations.
85
- config.active_record.dump_schema_after_migration = false
86
- end
File without changes