recaptcha 5.7.0 → 5.9.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 +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +59 -19
- data/lib/recaptcha/adapters/controller_methods.rb +5 -3
- data/lib/recaptcha/configuration.rb +23 -5
- data/lib/recaptcha/helpers.rb +19 -9
- data/lib/recaptcha/version.rb +1 -1
- data/lib/recaptcha.rb +55 -7
- data/rails/locales/fr.yml +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b4e91df77500a77804749d34ac9690c4ddc9a8cfa30af5f2eae6b51c3d0a0e02
|
4
|
+
data.tar.gz: 2fa166f38a4e39ee6b2244bdfc91991b3bfc6d65360c6dd2e846d584352e3aab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: acfb0b7bf9d211b2571dfcd8ee5a8195dc149effee4a04fe609d116f869722e583f4a5737e7d21174f2ff3d0f08c1f73f845dd4859daa107500e272230562052
|
7
|
+
data.tar.gz: 8c64efbfd826ab4a817fdbd37ef8e199d6a7c661b0efc18f6e4c5b26aca45a4412c8c0b331772561fe478d1f88c88b1a0cc655471c3eb751883a767a852680cf
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -68,6 +68,14 @@ export RECAPTCHA_SITE_KEY = '6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy'
|
|
68
68
|
export RECAPTCHA_SECRET_KEY = '6Lc6BAAAAAAAAKN3DRm6VA_xxxxxxxxxxxxxxxxx'
|
69
69
|
```
|
70
70
|
|
71
|
+
If you have an Enterprise API key:
|
72
|
+
|
73
|
+
```shell
|
74
|
+
export RECAPTCHA_ENTERPRISE = 'true'
|
75
|
+
export RECAPTCHA_ENTERPRISE_API_KEY = 'AIzvFyE3TU-g4K_Kozr9F1smEzZSGBVOfLKyupA'
|
76
|
+
export RECAPTCHA_ENTERPRISE_PROJECT_ID = 'my-project'
|
77
|
+
```
|
78
|
+
|
71
79
|
Add `recaptcha_tags` to the forms you want to protect:
|
72
80
|
|
73
81
|
```erb
|
@@ -159,16 +167,18 @@ you like.
|
|
159
167
|
|
160
168
|
Some of the options available:
|
161
169
|
|
162
|
-
| Option
|
163
|
-
|
164
|
-
| `:model`
|
165
|
-
| `:attribute`
|
166
|
-
| `:message`
|
167
|
-
| `:secret_key`
|
168
|
-
| `:
|
169
|
-
| `:
|
170
|
-
| `:
|
171
|
-
| `:
|
170
|
+
| Option | Description |
|
171
|
+
|---------------------------|-------------|
|
172
|
+
| `:model` | Model to set errors.
|
173
|
+
| `:attribute` | Model attribute to receive errors. (default: `:base`)
|
174
|
+
| `:message` | Custom error message.
|
175
|
+
| `:secret_key` | Override the secret API key from the configuration.
|
176
|
+
| `:enterprise_api_key` | Override the Enterprise API key from the configuration.
|
177
|
+
| `:enterprise_project_id ` | Override the Enterprise project ID from the configuration.
|
178
|
+
| `:timeout` | The number of seconds to wait for reCAPTCHA servers before give up. (default: `3`)
|
179
|
+
| `:response` | Custom response parameter. (default: `params['g-recaptcha-response-data']`)
|
180
|
+
| `:hostname` | Expected hostname or a callable that validates the hostname, see [domain validation](https://developers.google.com/recaptcha/docs/domain_validation) and [hostname](https://developers.google.com/recaptcha/docs/verify#api-response) docs. (default: `nil`, but can be changed by setting `config.hostname`)
|
181
|
+
| `:env` | Current environment. The request to verify will be skipped if the environment is specified in configuration under `skip_verify_env`
|
172
182
|
|
173
183
|
|
174
184
|
### `invisible_recaptcha_tags`
|
@@ -376,17 +386,19 @@ then you can either:
|
|
376
386
|
2. write and specify a custom `callback` function. You may also want to pass `element: false` if you
|
377
387
|
don't have a use for the hidden input element.
|
378
388
|
|
379
|
-
Note that you cannot submit/verify the same response token more than once or you
|
380
|
-
`timeout-or-duplicate` error code. If you need reset the captcha and
|
381
|
-
then you need to call `grecaptcha.execute(…)`
|
382
|
-
|
383
|
-
|
384
|
-
|
389
|
+
Note that you cannot submit/verify the same response token more than once or you
|
390
|
+
will get a `timeout-or-duplicate` error code. If you need reset the captcha and
|
391
|
+
generate a new response token, then you need to call `grecaptcha.execute(…)` or
|
392
|
+
`grecaptcha.enterprise.execute(…)` again. This helper provides a JavaScript
|
393
|
+
method (for each action) named `executeRecaptchaFor{action}` to make this
|
394
|
+
easier. That is the same method that is invoked immediately. It simply calls
|
395
|
+
`grecaptcha.execute` or `grecaptcha.enterprise.execute` again and then calls the
|
396
|
+
`callback` function with the response token.
|
385
397
|
|
386
398
|
You will also get a `timeout-or-duplicate` error if too much time has passed between getting the
|
387
399
|
response token and verifying it. This can easily happen with large forms that take the user a couple
|
388
400
|
minutes to complete. Unlike v2, where you can use the `expired-callback` to be notified when the
|
389
|
-
response
|
401
|
+
response expires, v3 appears to provide no such callback. See also
|
390
402
|
[1](https://github.com/google/recaptcha/issues/281) and
|
391
403
|
[2](https://stackoverflow.com/questions/54437745/recaptcha-v3-how-to-deal-with-expired-token-after-idle).
|
392
404
|
|
@@ -446,7 +458,7 @@ According to https://developers.google.com/recaptcha/docs/v3#placement,
|
|
446
458
|
|
447
459
|
> Note: You can execute reCAPTCHA as many times as you'd like with different actions on the same page.
|
448
460
|
|
449
|
-
You will need to verify each action individually with separate call to `verify_recaptcha`.
|
461
|
+
You will need to verify each action individually with a separate call to `verify_recaptcha`.
|
450
462
|
|
451
463
|
```ruby
|
452
464
|
result_a = verify_recaptcha(action: 'a')
|
@@ -506,14 +518,20 @@ Recaptcha.configuration.skip_verify_env.delete("test")
|
|
506
518
|
Recaptcha.configure do |config|
|
507
519
|
config.site_key = '6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy'
|
508
520
|
config.secret_key = '6Lc6BAAAAAAAAKN3DRm6VA_xxxxxxxxxxxxxxxxx'
|
521
|
+
|
509
522
|
# Uncomment the following line if you are using a proxy server:
|
510
523
|
# config.proxy = 'http://myproxy.com.au:8080'
|
524
|
+
|
525
|
+
# Uncomment the following lines if you are using the Enterprise API:
|
526
|
+
# config.enterprise = true
|
527
|
+
# config.enterprise_api_key = 'AIzvFyE3TU-g4K_Kozr9F1smEzZSGBVOfLKyupA'
|
528
|
+
# config.enterprise_project_id = 'my-project'
|
511
529
|
end
|
512
530
|
```
|
513
531
|
|
514
532
|
### Recaptcha.with_configuration
|
515
533
|
|
516
|
-
For temporary overwrites (not thread
|
534
|
+
For temporary overwrites (not thread-safe).
|
517
535
|
|
518
536
|
```ruby
|
519
537
|
Recaptcha.with_configuration(site_key: '12345') do
|
@@ -533,6 +551,28 @@ recaptcha_tags site_key: '6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy'
|
|
533
551
|
verify_recaptcha secret_key: '6Lc6BAAAAAAAAKN3DRm6VA_xxxxxxxxxxxxxxxxx'
|
534
552
|
```
|
535
553
|
|
554
|
+
|
555
|
+
## hCaptcha support
|
556
|
+
|
557
|
+
[hCaptcha](https://hcaptcha.com) is an alternative service providing reCAPTCHA API.
|
558
|
+
|
559
|
+
To use hCaptcha:
|
560
|
+
1. Set a site and a secret key as usual
|
561
|
+
2. Set two options in `verify_url` and `api_service_url` pointing to hCaptcha API endpoints.
|
562
|
+
3. Disable a response limit check by setting a `response_limit` to the negative or large enough value (reCAPTCHA is limited by 4000 characters).
|
563
|
+
4. It is not required to change a parameter name as [official docs suggest](https://docs.hcaptcha.com/switch) because API handles standard `g-recaptcha` for compatibility.
|
564
|
+
|
565
|
+
```ruby
|
566
|
+
# config/initializers/recaptcha.rb
|
567
|
+
Recaptcha.configure do |config|
|
568
|
+
config.site_key = '6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy'
|
569
|
+
config.secret_key = '6Lc6BAAAAAAAAKN3DRm6VA_xxxxxxxxxxxxxxxxx'
|
570
|
+
config.verify_url = 'https://hcaptcha.com/siteverify'
|
571
|
+
config.api_server_url = 'https://hcaptcha.com/1/api.js'
|
572
|
+
config.response_limit = -1
|
573
|
+
end
|
574
|
+
```
|
575
|
+
|
536
576
|
## Misc
|
537
577
|
- Check out the [wiki](https://github.com/ambethia/recaptcha/wiki) and leave whatever you found valuable there.
|
538
578
|
- [Add multiple widgets to the same page](https://github.com/ambethia/recaptcha/wiki/Add-multiple-widgets-to-the-same-page)
|
@@ -83,10 +83,12 @@ module Recaptcha
|
|
83
83
|
# @return [String] A response token if one was passed in the params; otherwise, `''`
|
84
84
|
def recaptcha_response_token(action = nil)
|
85
85
|
response_param = params['g-recaptcha-response-data'] || params['g-recaptcha-response']
|
86
|
-
if response_param
|
87
|
-
|
86
|
+
response_param = response_param[action] if action && response_param.respond_to?(:key?)
|
87
|
+
|
88
|
+
if response_param.is_a?(String)
|
89
|
+
response_param
|
88
90
|
else
|
89
|
-
|
91
|
+
''
|
90
92
|
end
|
91
93
|
end
|
92
94
|
end
|
@@ -31,11 +31,14 @@ module Recaptcha
|
|
31
31
|
#
|
32
32
|
class Configuration
|
33
33
|
DEFAULTS = {
|
34
|
-
'
|
35
|
-
'
|
34
|
+
'free_server_url' => 'https://www.recaptcha.net/recaptcha/api.js',
|
35
|
+
'enterprise_server_url' => 'https://www.recaptcha.net/recaptcha/enterprise.js',
|
36
|
+
'free_verify_url' => 'https://www.recaptcha.net/recaptcha/api/siteverify',
|
37
|
+
'enterprise_verify_url' => 'https://recaptchaenterprise.googleapis.com/v1beta1/projects'
|
36
38
|
}.freeze
|
37
39
|
|
38
|
-
attr_accessor :default_env, :skip_verify_env, :
|
40
|
+
attr_accessor :default_env, :skip_verify_env, :proxy, :secret_key, :site_key, :handle_timeouts_gracefully, :hostname
|
41
|
+
attr_accessor :enterprise, :enterprise_api_key, :enterprise_project_id, :response_limit
|
39
42
|
attr_writer :api_server_url, :verify_url
|
40
43
|
|
41
44
|
def initialize #:nodoc:
|
@@ -45,8 +48,15 @@ module Recaptcha
|
|
45
48
|
|
46
49
|
@secret_key = ENV['RECAPTCHA_SECRET_KEY']
|
47
50
|
@site_key = ENV['RECAPTCHA_SITE_KEY']
|
51
|
+
|
52
|
+
@enterprise = ENV['RECAPTCHA_ENTERPRISE'] == 'true'
|
53
|
+
@enterprise_api_key = ENV['RECAPTCHA_ENTERPRISE_API_KEY']
|
54
|
+
@enterprise_project_id = ENV['RECAPTCHA_ENTERPRISE_PROJECT_ID']
|
55
|
+
|
48
56
|
@verify_url = nil
|
49
57
|
@api_server_url = nil
|
58
|
+
|
59
|
+
@response_limit = 4000
|
50
60
|
end
|
51
61
|
|
52
62
|
def secret_key!
|
@@ -57,12 +67,20 @@ module Recaptcha
|
|
57
67
|
site_key || raise(RecaptchaError, "No site key specified.")
|
58
68
|
end
|
59
69
|
|
70
|
+
def enterprise_api_key!
|
71
|
+
enterprise_api_key || raise(RecaptchaError, "No Enterprise API key specified.")
|
72
|
+
end
|
73
|
+
|
74
|
+
def enterprise_project_id!
|
75
|
+
enterprise_project_id || raise(RecaptchaError, "No Enterprise project ID specified.")
|
76
|
+
end
|
77
|
+
|
60
78
|
def api_server_url
|
61
|
-
@api_server_url || DEFAULTS.fetch('
|
79
|
+
@api_server_url || (enterprise ? DEFAULTS.fetch('enterprise_server_url') : DEFAULTS.fetch('free_server_url'))
|
62
80
|
end
|
63
81
|
|
64
82
|
def verify_url
|
65
|
-
@verify_url || DEFAULTS.fetch('
|
83
|
+
@verify_url || (enterprise ? DEFAULTS.fetch('enterprise_verify_url') : DEFAULTS.fetch('free_verify_url'))
|
66
84
|
end
|
67
85
|
end
|
68
86
|
end
|
data/lib/recaptcha/helpers.rb
CHANGED
@@ -174,7 +174,8 @@ module Recaptcha
|
|
174
174
|
|
175
175
|
# v3
|
176
176
|
|
177
|
-
# Renders a script that calls `grecaptcha.execute`
|
177
|
+
# Renders a script that calls `grecaptcha.execute` or
|
178
|
+
# `grecaptcha.enterprise.execute` for the given `site_key` and `action` and
|
178
179
|
# calls the `callback` with the resulting response token.
|
179
180
|
private_class_method def self.recaptcha_v3_inline_script(site_key, action, callback, id, options = {})
|
180
181
|
nonce = options[:nonce]
|
@@ -185,8 +186,8 @@ module Recaptcha
|
|
185
186
|
// Define function so that we can call it again later if we need to reset it
|
186
187
|
// This executes reCAPTCHA and then calls our callback.
|
187
188
|
function #{recaptcha_v3_execute_function_name(action)}() {
|
188
|
-
|
189
|
-
|
189
|
+
#{recaptcha_ready_method_name}(function() {
|
190
|
+
#{recaptcha_execute_method_name}('#{site_key}', {action: '#{action}'}).then(function(token) {
|
190
191
|
#{callback}('#{id}', token)
|
191
192
|
});
|
192
193
|
});
|
@@ -199,8 +200,8 @@ module Recaptcha
|
|
199
200
|
// Returns a Promise that resolves with the response token.
|
200
201
|
async function #{recaptcha_v3_async_execute_function_name(action)}() {
|
201
202
|
return new Promise((resolve, reject) => {
|
202
|
-
|
203
|
-
resolve(await
|
203
|
+
#{recaptcha_ready_method_name}(async function() {
|
204
|
+
resolve(await #{recaptcha_execute_method_name}('#{site_key}', {action: '#{action}'}))
|
204
205
|
});
|
205
206
|
})
|
206
207
|
};
|
@@ -217,8 +218,8 @@ module Recaptcha
|
|
217
218
|
<<-HTML
|
218
219
|
<script#{nonce_attr}>
|
219
220
|
function #{recaptcha_v3_execute_function_name(action)}() {
|
220
|
-
|
221
|
-
|
221
|
+
#{recaptcha_ready_method_name}(function() {
|
222
|
+
#{recaptcha_execute_method_name}('#{site_key}', {action: '#{action}'}).then(function(token) {
|
222
223
|
#{callback}('#{id}', token)
|
223
224
|
});
|
224
225
|
});
|
@@ -251,8 +252,9 @@ module Recaptcha
|
|
251
252
|
recaptcha_v3_inline_script?(options)
|
252
253
|
end
|
253
254
|
|
254
|
-
# Returns the name of the JavaScript function that actually executes the
|
255
|
-
# grecaptcha.execute
|
255
|
+
# Returns the name of the JavaScript function that actually executes the
|
256
|
+
# reCAPTCHA code (calls `grecaptcha.execute` or
|
257
|
+
# `grecaptcha.enterprise.execute`). You can call it again later to reset it.
|
256
258
|
def self.recaptcha_v3_execute_function_name(action)
|
257
259
|
"executeRecaptchaFor#{sanitize_action_for_js(action)}"
|
258
260
|
end
|
@@ -296,6 +298,14 @@ module Recaptcha
|
|
296
298
|
HTML
|
297
299
|
end
|
298
300
|
|
301
|
+
def self.recaptcha_execute_method_name
|
302
|
+
Recaptcha.configuration.enterprise ? "grecaptcha.enterprise.execute" : "grecaptcha.execute"
|
303
|
+
end
|
304
|
+
|
305
|
+
def self.recaptcha_ready_method_name
|
306
|
+
Recaptcha.configuration.enterprise ? "grecaptcha.enterprise.ready" : "grecaptcha.ready"
|
307
|
+
end
|
308
|
+
|
299
309
|
private_class_method def self.default_callback_required?(options)
|
300
310
|
options[:callback] == 'invisibleRecaptchaSubmit' &&
|
301
311
|
!Recaptcha.skip_env?(options[:env]) &&
|
data/lib/recaptcha/version.rb
CHANGED
data/lib/recaptcha.rb
CHANGED
@@ -14,7 +14,6 @@ end
|
|
14
14
|
|
15
15
|
module Recaptcha
|
16
16
|
DEFAULT_TIMEOUT = 3
|
17
|
-
RESPONSE_LIMIT = 4000
|
18
17
|
|
19
18
|
class RecaptchaError < StandardError
|
20
19
|
end
|
@@ -56,15 +55,48 @@ module Recaptcha
|
|
56
55
|
end
|
57
56
|
|
58
57
|
def self.invalid_response?(resp)
|
59
|
-
resp.empty? || resp.length >
|
58
|
+
resp.empty? || resp.length > configuration.response_limit
|
60
59
|
end
|
61
60
|
|
62
61
|
def self.verify_via_api_call(response, options)
|
62
|
+
if Recaptcha.configuration.enterprise
|
63
|
+
verify_via_api_call_enterprise(response, options)
|
64
|
+
else
|
65
|
+
verify_via_api_call_free(response, options)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.verify_via_api_call_enterprise(response, options)
|
70
|
+
site_key = options.fetch(:site_key) { configuration.site_key! }
|
71
|
+
api_key = options.fetch(:enterprise_api_key) { configuration.enterprise_api_key! }
|
72
|
+
project_id = options.fetch(:enterprise_project_id) { configuration.enterprise_project_id! }
|
73
|
+
|
74
|
+
query_params = { 'key' => api_key }
|
75
|
+
body = { 'event' => { 'token' => response, 'siteKey' => site_key } }
|
76
|
+
body['event']['expectedAction'] = options[:action] if options.key?(:action)
|
77
|
+
body['event']['userIpAddress'] = options[:remote_ip] if options.key?(:remote_ip)
|
78
|
+
|
79
|
+
reply = api_verification_enterprise(query_params, body, project_id, timeout: options[:timeout])
|
80
|
+
token_properties = reply['tokenProperties']
|
81
|
+
success = !token_properties.nil? &&
|
82
|
+
token_properties['valid'].to_s == 'true' &&
|
83
|
+
hostname_valid?(token_properties['hostname'], options[:hostname]) &&
|
84
|
+
action_valid?(token_properties['action'], options[:action]) &&
|
85
|
+
score_above_threshold?(reply['score'], options[:minimum_score])
|
86
|
+
|
87
|
+
if options[:with_reply] == true
|
88
|
+
return success, reply
|
89
|
+
else
|
90
|
+
return success
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.verify_via_api_call_free(response, options)
|
63
95
|
secret_key = options.fetch(:secret_key) { configuration.secret_key! }
|
64
96
|
verify_hash = { 'secret' => secret_key, 'response' => response }
|
65
97
|
verify_hash['remoteip'] = options[:remote_ip] if options.key?(:remote_ip)
|
66
98
|
|
67
|
-
reply =
|
99
|
+
reply = api_verification_free(verify_hash, timeout: options[:timeout])
|
68
100
|
success = reply['success'].to_s == 'true' &&
|
69
101
|
hostname_valid?(reply['hostname'], options[:hostname]) &&
|
70
102
|
action_valid?(reply['action'], options[:action]) &&
|
@@ -105,7 +137,7 @@ module Recaptcha
|
|
105
137
|
end
|
106
138
|
end
|
107
139
|
|
108
|
-
def self.
|
140
|
+
def self.http_client_for(uri:, timeout: nil)
|
109
141
|
timeout ||= DEFAULT_TIMEOUT
|
110
142
|
http = if configuration.proxy
|
111
143
|
proxy_server = URI.parse(configuration.proxy)
|
@@ -113,12 +145,28 @@ module Recaptcha
|
|
113
145
|
else
|
114
146
|
Net::HTTP
|
115
147
|
end
|
148
|
+
instance = http.new(uri.host, uri.port)
|
149
|
+
instance.read_timeout = instance.open_timeout = timeout
|
150
|
+
instance.use_ssl = true if uri.port == 443
|
151
|
+
|
152
|
+
instance
|
153
|
+
end
|
154
|
+
|
155
|
+
def self.api_verification_free(verify_hash, timeout: nil)
|
116
156
|
query = URI.encode_www_form(verify_hash)
|
117
157
|
uri = URI.parse(configuration.verify_url + '?' + query)
|
118
|
-
http_instance =
|
119
|
-
http_instance.read_timeout = http_instance.open_timeout = timeout
|
120
|
-
http_instance.use_ssl = true if uri.port == 443
|
158
|
+
http_instance = http_client_for(uri: uri, timeout: timeout)
|
121
159
|
request = Net::HTTP::Get.new(uri.request_uri)
|
122
160
|
JSON.parse(http_instance.request(request).body)
|
123
161
|
end
|
162
|
+
|
163
|
+
def self.api_verification_enterprise(query_params, body, project_id, timeout: nil)
|
164
|
+
query = URI.encode_www_form(query_params)
|
165
|
+
uri = URI.parse(configuration.verify_url + "/#{project_id}/assessments" + '?' + query)
|
166
|
+
http_instance = http_client_for(uri: uri, timeout: timeout)
|
167
|
+
request = Net::HTTP::Post.new(uri.request_uri)
|
168
|
+
request['Content-Type'] = 'application/json; charset=utf-8'
|
169
|
+
request.body = JSON.generate(body)
|
170
|
+
JSON.parse(http_instance.request(request).body)
|
171
|
+
end
|
124
172
|
end
|
data/rails/locales/fr.yml
CHANGED
@@ -2,4 +2,4 @@ fr:
|
|
2
2
|
recaptcha:
|
3
3
|
errors:
|
4
4
|
verification_failed: La vérification reCAPTCHA a échoué, veuillez essayer à nouveau.
|
5
|
-
recaptcha_unreachable: Oops, nous n'avons pas
|
5
|
+
recaptcha_unreachable: Oops, nous n'avons pas pu valider votre réponse reCAPTCHA. Veuillez essayer à nouveau.
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: recaptcha
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jason L Perry
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-03-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|
@@ -176,7 +176,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
176
176
|
- !ruby/object:Gem::Version
|
177
177
|
version: '0'
|
178
178
|
requirements: []
|
179
|
-
rubygems_version: 3.1.
|
179
|
+
rubygems_version: 3.1.6
|
180
180
|
signing_key:
|
181
181
|
specification_version: 4
|
182
182
|
summary: Helpers for the reCAPTCHA API
|