cloudflare-turnstile-rails 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7a86ed02899dc2410a686143de9c188b3d15cfef8e4928bc87cc00bd1741fb58
4
- data.tar.gz: 566026c1569d205f3a91beec381ec29729e6afe78084b9a6fb60ef4ceb0b3fad
3
+ metadata.gz: 7bdf250ea2814264e89921898dcc1c38b968596fe59e9423914b2d245ee8607c
4
+ data.tar.gz: e72026fff680a00cccfdb6134fbe9dafe0658abc8c135b0c5a4bc73404a1a104
5
5
  SHA512:
6
- metadata.gz: 5a5a699a90fcb1a704de8a46ec57571c5219794d853d94a0d7bbac05e3e91dfa7ee20ddefaa1245d246a8b64bba082f58800821db4e2d4accc3ac16252eca204
7
- data.tar.gz: e3655f9b725839b328f3699fd7dfb8def8f91d8011bd10dd4c8e4904cc6a12c61453a85bde4c666cd8d26185cb623da406ec634828785ff27d5833b6e5854594
6
+ metadata.gz: dfe05337cc155cd855b9297c8512d1c173069862a33f3061b685e41db3ec4ac7c20e86466d2c90d82d10795ccb0560b64177a344a5098885506efc53f614872d
7
+ data.tar.gz: aa3d5dacc7e140dedf475881f61522480b30c87e39bf1b228999ed5244fbe33cf1f02af3ffa6575bc2e0a09acb78221375b2fe82863dfcf13274398bac631d3d
data/README.md CHANGED
@@ -11,6 +11,9 @@ Cloudflare Turnstile gem for Ruby on Rails with built-in Turbo and Turbolinks su
11
11
 
12
12
  Supports **Rails 5.0 → latest** and **Ruby 2.6 → latest**, with the full Rails/Ruby matrix tested daily in CI.
13
13
 
14
+ > [!TIP]
15
+ > **Using Devise?** [devise-cloudflare-turnstile](https://github.com/vkononov/devise-cloudflare-turnstile) protects every Devise form (sign in, sign up, password reset, and more) with no changes to your Devise views or controllers. It is built on this gem and shares its configuration, so you can use both to protect Devise and non-Devise forms in the same app.
16
+
14
17
  [!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/yellow_img.png)](https://www.buymeacoffee.com/vkononov)
15
18
 
16
19
  ## Features
@@ -184,6 +187,16 @@ Supports **Rails 5.0 → latest** and **Ruby 2.6 → latest**, with the full Rai
184
187
  end
185
188
  ```
186
189
 
190
+ * Requests to Cloudflare time out after 5 seconds to connect, read, or write, so a slow Cloudflare can't tie up your app's request threads for long. A timeout raises `Cloudflare::Turnstile::Rails::ConfigurationError`. To change the limits:
191
+
192
+ ```ruby
193
+ Cloudflare::Turnstile::Rails.configure do |config|
194
+ config.open_timeout = 5 # connecting to siteverify
195
+ config.read_timeout = 5 # reading the response
196
+ config.write_timeout = 5 # sending the verification POST
197
+ end
198
+ ```
199
+
187
200
  #### Advanced Validation
188
201
 
189
202
  * To inspect the entire verification payload, use `verify_turnstile`. It returns a `VerificationResponse` object with detailed information:
@@ -203,6 +216,8 @@ Supports **Rails 5.0 → latest** and **Ruby 2.6 → latest**, with the full Rai
203
216
  end
204
217
  ```
205
218
 
219
+ * A missing, malformed, or oversized token fails immediately with `missing-input-response` or `invalid-input-response`, without a request to Cloudflare. The `VerificationResponse` for these failures only has `success` and `error-codes`; it doesn't carry `hostname`, `challenge_ts`, or the other fields a real siteverify call returns.
220
+
206
221
  * The `VerificationResponse` object contains the raw response from Cloudflare:
207
222
 
208
223
  ```ruby
@@ -3,7 +3,8 @@ module Cloudflare
3
3
  module Rails
4
4
  class Configuration
5
5
  attr_writer :script_url
6
- attr_accessor :site_key, :secret_key, :render, :onload, :default_data, :auto_populate_response_in_test_env
6
+ attr_accessor :site_key, :secret_key, :render, :onload, :default_data, :auto_populate_response_in_test_env,
7
+ :open_timeout, :read_timeout, :write_timeout
7
8
 
8
9
  def initialize
9
10
  @script_url = Cloudflare::SCRIPT_URL
@@ -13,6 +14,9 @@ module Cloudflare
13
14
  @onload = nil
14
15
  @default_data = {}
15
16
  @auto_populate_response_in_test_env = true
17
+ @open_timeout = 5
18
+ @read_timeout = 5
19
+ @write_timeout = 5
16
20
  end
17
21
 
18
22
  # Dynamically build the URL every time, so that
@@ -11,6 +11,9 @@ module Cloudflare
11
11
  # Default hidden input field name for Turnstile token submission
12
12
  RESPONSE_FIELD_NAME = 'cf-turnstile-response'.freeze
13
13
 
14
+ # Longest token siteverify accepts; anything longer is rejected without a request
15
+ MAX_RESPONSE_LENGTH = 2048
16
+
14
17
  # Default CSS class applied to Turnstile widget containers
15
18
  WIDGET_CLASS = 'cf-turnstile'.freeze
16
19
  end
@@ -2,6 +2,7 @@ require 'net/http'
2
2
  require 'openssl'
3
3
 
4
4
  require_relative 'constants/cloudflare'
5
+ require_relative 'constants/error_code'
5
6
 
6
7
  module Cloudflare
7
8
  module Turnstile
@@ -48,6 +49,10 @@ module Cloudflare
48
49
 
49
50
  module Verification
50
51
  def self.verify(response: nil, secret: nil, remoteip: nil, idempotency_key: nil) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
52
+ if response.is_a?(String) && !response.valid_encoding?
53
+ return VerificationResponse.new('success' => false, 'error-codes' => [ErrorCode::INVALID_INPUT_RESPONSE])
54
+ end
55
+
51
56
  response = 'dummy-response' if auto_populate_test_response?(response)
52
57
 
53
58
  secret ||= Rails.configuration.secret_key
@@ -55,6 +60,14 @@ module Cloudflare
55
60
  raise ConfigurationError, ErrorMessage.for(ErrorCode::MISSING_INPUT_SECRET)
56
61
  end
57
62
 
63
+ local_error = if missing_response?(response)
64
+ ErrorCode::MISSING_INPUT_RESPONSE
65
+ elsif invalid_response?(response)
66
+ ErrorCode::INVALID_INPUT_RESPONSE
67
+ end
68
+
69
+ return VerificationResponse.new('success' => false, 'error-codes' => [local_error]) if local_error
70
+
58
71
  body = { 'secret' => secret, 'response' => response }
59
72
  body['remoteip'] = remoteip if remoteip
60
73
  body['idempotency_key'] = idempotency_key if idempotency_key
@@ -65,17 +78,20 @@ module Cloudflare
65
78
 
66
79
  http = Net::HTTP.new(uri.host, uri.port)
67
80
  http.use_ssl = true
81
+ http.open_timeout = Rails.configuration.open_timeout
82
+ http.read_timeout = Rails.configuration.read_timeout
83
+ http.write_timeout = Rails.configuration.write_timeout
68
84
  http.verify_mode = OpenSSL::SSL::VERIFY_PEER
69
85
  http.cert_store = OpenSSL::X509::Store.new
70
86
  http.cert_store.set_default_paths
71
87
 
72
88
  begin
73
89
  res = http.request(request)
74
- rescue Net::OpenTimeout, Net::ReadTimeout => e
90
+ rescue Net::OpenTimeout, Net::ReadTimeout, Net::WriteTimeout, Errno::ETIMEDOUT => e
75
91
  raise ConfigurationError, "Turnstile verification timed out: #{e.message}"
76
92
  rescue OpenSSL::SSL::SSLError => e
77
93
  raise ConfigurationError, "SSL verification failed: #{e.message}"
78
- rescue SocketError, Errno::ECONNREFUSED => e
94
+ rescue SocketError, EOFError, Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::EHOSTUNREACH => e
79
95
  raise ConfigurationError, "Network error during Turnstile verification: #{e.message}"
80
96
  end
81
97
 
@@ -89,11 +105,21 @@ module Cloudflare
89
105
  end
90
106
 
91
107
  def self.auto_populate_test_response?(response)
92
- (response.nil? || response.strip.empty?) &&
108
+ (response.nil? || (response.is_a?(String) && response.strip.empty?)) &&
93
109
  ::Rails.env.test? &&
94
110
  Rails.configuration.auto_populate_response_in_test_env
95
111
  end
96
112
  private_class_method :auto_populate_test_response?
113
+
114
+ def self.missing_response?(response)
115
+ response.nil? || response == ''
116
+ end
117
+ private_class_method :missing_response?
118
+
119
+ def self.invalid_response?(response)
120
+ !response.is_a?(String) || response.strip.empty? || response.length > Cloudflare::MAX_RESPONSE_LENGTH
121
+ end
122
+ private_class_method :invalid_response?
97
123
  end
98
124
  end
99
125
  end
@@ -1,7 +1,7 @@
1
1
  module Cloudflare
2
2
  module Turnstile
3
3
  module Rails
4
- VERSION = '1.2.0'.freeze
4
+ VERSION = '1.3.0'.freeze
5
5
  end
6
6
  end
7
7
  end
@@ -25,6 +25,12 @@ Cloudflare::Turnstile::Rails.configure do |config|
25
25
  # language: -> { I18n.locale }
26
26
  # }
27
27
 
28
+ # Optional: Seconds to wait when connecting to, and reading from, Cloudflare's siteverify endpoint.
29
+ # Keep these short so a slow Cloudflare can't hold your app's request threads for long.
30
+ # config.open_timeout = 5 # connecting to siteverify
31
+ # config.read_timeout = 5 # reading the response
32
+ # config.write_timeout = 5 # sending the verification POST
33
+
28
34
  # In the Rails Test environment, automatically fill in a dummy response if none was provided.
29
35
  # This lets you keep existing controller tests without having to add
30
36
  # params["cf-turnstile-response"] manually in every test.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cloudflare-turnstile-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vadim Kononov
@@ -23,8 +23,9 @@ dependencies:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
25
  version: '5.0'
26
- description: Integrates Cloudflare Turnstile into Ruby on Rails applications, transparently
27
- reloads on Turbo and Turbolinks events, and embeds CSP-nonce-compliant scripts.
26
+ description: Cloudflare Turnstile CAPTCHA, a reCAPTCHA alternative, for Ruby on Rails.
27
+ View and controller helpers with Turbo, Turbo Streams, Turbolinks, CSP nonce and
28
+ i18n support.
28
29
  email:
29
30
  - vadim@konoson.com
30
31
  executables: []
@@ -83,8 +84,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
83
84
  - !ruby/object:Gem::Version
84
85
  version: '0'
85
86
  requirements: []
86
- rubygems_version: 3.7.2
87
+ rubygems_version: 4.0.20
87
88
  specification_version: 4
88
- summary: Cloudflare Turnstile gem for Rails with built-in Turbo and Turbolinks support
89
- and CSP compliance
89
+ summary: Cloudflare Turnstile CAPTCHA for Rails with Turbo, Turbolinks and CSP support
90
90
  test_files: []