recaptcha 5.12.3 → 5.21.2
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 +39 -0
- data/README.md +24 -6
- data/lib/recaptcha/adapters/controller_methods.rb +27 -2
- data/lib/recaptcha/configuration.rb +5 -2
- data/lib/recaptcha/helpers.rb +18 -11
- data/lib/recaptcha/reply.rb +118 -0
- data/lib/recaptcha/version.rb +1 -1
- data/lib/recaptcha.rb +25 -58
- data/rails/locales/de.yml +5 -0
- data/rails/locales/es.yml +5 -0
- data/rails/locales/it.yml +5 -0
- data/rails/locales/pt-BR.yml +5 -0
- data/rails/locales/pt.yml +5 -0
- metadata +11 -18
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f5915ff80a85bae6b80042f1d8a1d3e622470c74d8a037ebf0564260431d2030
|
|
4
|
+
data.tar.gz: c877f9e53cd0d5ba551b0bd2d5cb6e381c6baa5d7e82f8b10d9b11c0d518cd0c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1bac93fe24a496e6d75f6515437ab3ca9c1589a1d6fbc61453833d9747910145138d00a4a8c42f648a3a46449a0dca2629333fa038689ad0e1f44a446bfd84f9
|
|
7
|
+
data.tar.gz: 95f37a636f308264a9e51ca2ca6854facd7ee0d692186272090ed60b02deccb70b00d2d9f3024dedaf040585664d688f163fa0e0ed756b36710856f7093c4d40
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,44 @@
|
|
|
1
1
|
## Next
|
|
2
2
|
|
|
3
|
+
## 5.21.2
|
|
4
|
+
* make env fall back to Rails.env if it is unset
|
|
5
|
+
|
|
6
|
+
## 5.21.1
|
|
7
|
+
* Fix flash not being updated when responding to :turbo_stream requests
|
|
8
|
+
|
|
9
|
+
## 5.21.0
|
|
10
|
+
* add referer header to #api_verification_entreprise
|
|
11
|
+
|
|
12
|
+
## 5.20.0
|
|
13
|
+
* turn recpatch reply into a object with logic
|
|
14
|
+
|
|
15
|
+
## 5.19.0
|
|
16
|
+
* require a minimum lenght of 100 for responses, configured via response_minimum
|
|
17
|
+
|
|
18
|
+
## 5.18.0
|
|
19
|
+
* Add key setup to v3 example in README
|
|
20
|
+
* Remove unnecessary id from textarea - This was unused and may cause accessability concerns if there is more than one recaptcha on the page due to multiple elements with the same id
|
|
21
|
+
* Update to latest version of rubocop
|
|
22
|
+
* Drop support for Ruby 2.7; add Ruby 3.3
|
|
23
|
+
* Add i18n: de, es, it, pt, pt-BR
|
|
24
|
+
* Added recaptcha_failure_reason
|
|
25
|
+
|
|
26
|
+
## 5.16.0
|
|
27
|
+
* Allow usage of `options[:turbo]` as well as `options[:turbolinks]` for `recaptcha_v3`
|
|
28
|
+
|
|
29
|
+
## 5.15.0
|
|
30
|
+
* Add 3.2 to the list of Ruby CI versions
|
|
31
|
+
* Add ability to submit verify_recaptcha via POST with JSON Body with `options[:json] = true`
|
|
32
|
+
|
|
33
|
+
## 5.14.0
|
|
34
|
+
* drop json dependency
|
|
35
|
+
|
|
36
|
+
## 5.13.1
|
|
37
|
+
* Permit actions as symbol
|
|
38
|
+
|
|
39
|
+
## 5.13.0
|
|
40
|
+
* Added option to ignore_no_element.
|
|
41
|
+
|
|
3
42
|
## 5.12.3
|
|
4
43
|
* Remove score fallback for enterprise
|
|
5
44
|
* Update enterprise tests to v1 assessment schema
|
data/README.md
CHANGED
|
@@ -78,6 +78,10 @@ export RECAPTCHA_ENTERPRISE_API_KEY = 'AIzvFyE3TU-g4K_Kozr9F1smEzZSGBVOfLKyup
|
|
|
78
78
|
export RECAPTCHA_ENTERPRISE_PROJECT_ID = 'my-project'
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
+
_note:_ you'll still have to provide `RECAPTCHA_SITE_KEY`, which will hold the value of your enterprise recaptcha key id. You will not need to provide a `RECAPTCHA_SECRET_KEY`, however.
|
|
82
|
+
|
|
83
|
+
`RECAPTCHA_ENTERPRISE_API_KEY` is the enterprise key of your Google Cloud Project, which you can generate here: https://console.cloud.google.com/apis/credentials.
|
|
84
|
+
|
|
81
85
|
Add `recaptcha_tags` to the forms you want to protect:
|
|
82
86
|
|
|
83
87
|
```erb
|
|
@@ -126,7 +130,7 @@ The following options are available:
|
|
|
126
130
|
| `:site_key` | Override site API key from configuration |
|
|
127
131
|
| `:error` | Override the error code returned from the reCAPTCHA API (default: `nil`) |
|
|
128
132
|
| `:size` | Specify a size (default: `nil`) |
|
|
129
|
-
| `:nonce` | Optional. Sets nonce attribute for script. Can be generated via `SecureRandom.base64(32)`. (default: `nil`) |
|
|
133
|
+
| `:nonce` | Optional. Sets nonce attribute for script. Can be generated via `SecureRandom.base64(32)`. Use `content_security_policy_nonce` if you have `config.content_security_policy_nonce_generator` set in Rails. (default: `nil`) |
|
|
130
134
|
| `:id` | Specify an html id attribute (default: `nil`) |
|
|
131
135
|
| `:callback` | Optional. Name of success callback function, executed when the user submits a successful response |
|
|
132
136
|
| `:expired_callback` | Optional. Name of expiration callback function, executed when the reCAPTCHA response expires and the user needs to re-verify. |
|
|
@@ -181,6 +185,7 @@ Some of the options available:
|
|
|
181
185
|
| `:response` | Custom response parameter. (default: `params['g-recaptcha-response-data']`)
|
|
182
186
|
| `: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`)
|
|
183
187
|
| `:env` | Current environment. The request to verify will be skipped if the environment is specified in configuration under `skip_verify_env`
|
|
188
|
+
| `:json` | Boolean; defaults to false; if true, will submit the verification request by POST with the request data in JSON
|
|
184
189
|
|
|
185
190
|
|
|
186
191
|
### `invisible_recaptcha_tags`
|
|
@@ -203,7 +208,7 @@ It also accepts most of the options that `recaptcha_tags` accepts, including the
|
|
|
203
208
|
| Option | Description |
|
|
204
209
|
|---------------------|-------------|
|
|
205
210
|
| `:site_key` | Override site API key from configuration |
|
|
206
|
-
| `:nonce` | Optional. Sets nonce attribute for script tag. Can be generated via `SecureRandom.base64(32)`. (default: `nil`) |
|
|
211
|
+
| `:nonce` | Optional. Sets nonce attribute for script tag. Can be generated via `SecureRandom.base64(32)`. Use `content_security_policy_nonce` if you have `config.content_security_policy_nonce_generator` set in Rails. (default: `nil`) |
|
|
207
212
|
| `:id` | Specify an html id attribute (default: `nil`) |
|
|
208
213
|
| `:script` | Same as setting both `:inline_script` and `:external_script`. If you only need one or the other, use `:inline_script` and `:external_script` instead. |
|
|
209
214
|
| `:callback` | Optional. Name of success callback function, executed when the user submits a successful response |
|
|
@@ -304,6 +309,14 @@ With v3, you can let all users log in without any intervention at all if their s
|
|
|
304
309
|
threshold, and only show a v2 checkbox recaptcha challenge (fall back to v2) if it is below the
|
|
305
310
|
threshold:
|
|
306
311
|
|
|
312
|
+
This example sets v2 keys through environment variables. For more information on how to set up keys, please refer to the [documentation here](#alternative-api-key-setup).
|
|
313
|
+
|
|
314
|
+
```bash
|
|
315
|
+
# .env
|
|
316
|
+
RECAPTCHA_SITE_KEY=6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy
|
|
317
|
+
RECAPTCHA_SECRET_KEY=6Lc6BAAAAAAAAKN3DRm6VA_xxxxxxxxxxxxxxxxx
|
|
318
|
+
```
|
|
319
|
+
|
|
307
320
|
```erb
|
|
308
321
|
…
|
|
309
322
|
<% if @show_checkbox_recaptcha %>
|
|
@@ -418,15 +431,17 @@ but only accepts the following options:
|
|
|
418
431
|
| Option | Description |
|
|
419
432
|
|---------------------|-------------|
|
|
420
433
|
| `:site_key` | Override site API key |
|
|
421
|
-
| `:action` | The name of the [reCAPTCHA action](https://developers.google.com/recaptcha/docs/v3#actions). Actions may only contain alphanumeric characters and
|
|
422
|
-
| `:nonce` | Optional. Sets nonce attribute for script. Can be generated via `SecureRandom.base64(32)`. (default: `nil`) |
|
|
434
|
+
| `:action` | The name of the [reCAPTCHA action](https://developers.google.com/recaptcha/docs/v3#actions). Actions are not case-sensitive and may only contain alphanumeric characters, slashes, and underscores, and must not be user-specific. |
|
|
435
|
+
| `:nonce` | Optional. Sets nonce attribute for script. Can be generated via `SecureRandom.base64(32)`. Use `content_security_policy_nonce` if you have `config.content_security_policy_nonce_generator` set in Rails. (default: `nil`) |
|
|
423
436
|
| `:callback` | Name of callback function to call with the token. When `element` is `:input`, this defaults to a function named `setInputWithRecaptchaResponseTokenFor#{sanitize_action(action)}` that sets the value of the hidden input to the token. |
|
|
424
437
|
| `:id` | Specify a unique `id` attribute for the `<input>` element if using `element: :input`. (default: `"g-recaptcha-response-data-"` + `action`) |
|
|
425
438
|
| `:name` | Specify a unique `name` attribute for the `<input>` element if using `element: :input`. (default: `g-recaptcha-response-data[action]`) |
|
|
426
439
|
| `:script` | Same as setting both `:inline_script` and `:external_script`. (default: `true`). |
|
|
427
440
|
| `:inline_script` | If `true`, adds an inline script tag that calls `grecaptcha.execute` for the given `site_key` and `action` and calls the `callback` with the resulting response token. Pass `false` if you want to handle calling `grecaptcha.execute` yourself. (default: `true`) |
|
|
428
441
|
| `:element` | The element to render, if any (default: `:input`)<br/>`:input`: Renders a hidden `<input type="hidden">` tag. The value of this will be set to the response token by the default `setInputWithRecaptchaResponseTokenFor{action}` callback.<br/>`false`: Doesn't render any tag. You'll have to add a custom callback that does something with the token. |
|
|
429
|
-
| `:
|
|
442
|
+
| `:turbo` | If `true`, calls the js function which executes reCAPTCHA after all the dependencies have been loaded. This cannot be used with the js param `:onload`. This makes reCAPTCHAv3 usable with turbo. |
|
|
443
|
+
| `:turbolinks` | Alias of `:turbo`. Will be deprecated soon. |
|
|
444
|
+
| `:ignore_no_element` | If `true`, adds null element checker for forms that can be removed from the page by javascript like modals with forms. (default: true) |
|
|
430
445
|
|
|
431
446
|
[JavaScript resource (api.js) parameters](https://developers.google.com/recaptcha/docs/invisible#js_param):
|
|
432
447
|
|
|
@@ -473,7 +488,7 @@ are passed as a hash under `params['g-recaptcha-response-data']` with the action
|
|
|
473
488
|
It is recommended to pass `external_script: false` on all but one of the calls to
|
|
474
489
|
`recaptcha` since you only need to include the script tag once for a given `site_key`.
|
|
475
490
|
|
|
476
|
-
## `recaptcha_reply`
|
|
491
|
+
## `recaptcha_reply` and `recaptcha_failure_reason`
|
|
477
492
|
|
|
478
493
|
After `verify_recaptcha` has been called, you can call `recaptcha_reply` to get the raw reply from recaptcha. This can allow you to get the exact score returned by recaptcha should you need it.
|
|
479
494
|
|
|
@@ -489,6 +504,8 @@ end
|
|
|
489
504
|
|
|
490
505
|
`recaptcha_reply` will return `nil` if the the reply was not yet fetched.
|
|
491
506
|
|
|
507
|
+
`recaptcha_failure_reason` will return information if verification failed. E.g. if params was wrong or api resulted some error-codes.
|
|
508
|
+
|
|
492
509
|
## I18n support
|
|
493
510
|
|
|
494
511
|
reCAPTCHA supports the I18n gem (it comes with English translations)
|
|
@@ -572,6 +589,7 @@ Recaptcha.configure do |config|
|
|
|
572
589
|
config.verify_url = 'https://hcaptcha.com/siteverify'
|
|
573
590
|
config.api_server_url = 'https://hcaptcha.com/1/api.js'
|
|
574
591
|
config.response_limit = 100000
|
|
592
|
+
config.response_minimum = 100
|
|
575
593
|
end
|
|
576
594
|
```
|
|
577
595
|
|
|
@@ -17,6 +17,11 @@ module Recaptcha
|
|
|
17
17
|
|
|
18
18
|
begin
|
|
19
19
|
verified = if Recaptcha.invalid_response?(recaptcha_response)
|
|
20
|
+
@_recaptcha_failure_reason = if recaptcha_response.nil?
|
|
21
|
+
"No recaptcha response/param(:action) found."
|
|
22
|
+
else
|
|
23
|
+
"Recaptcha response/param(:action) was invalid."
|
|
24
|
+
end
|
|
20
25
|
false
|
|
21
26
|
else
|
|
22
27
|
unless options[:skip_remote_ip]
|
|
@@ -26,10 +31,23 @@ module Recaptcha
|
|
|
26
31
|
|
|
27
32
|
success, @_recaptcha_reply =
|
|
28
33
|
Recaptcha.verify_via_api_call(recaptcha_response, options.merge(with_reply: true))
|
|
34
|
+
|
|
35
|
+
unless success
|
|
36
|
+
@_recaptcha_failure_reason =
|
|
37
|
+
if @_recaptcha_reply.score && @_recaptcha_reply.score.to_f < options[:minimum_score].to_f
|
|
38
|
+
"Recaptcha score didn't exceed the minimum: #{@_recaptcha_reply.score} < #{options[:minimum_score]}."
|
|
39
|
+
elsif @_recaptcha_reply.error_codes.any?
|
|
40
|
+
"Recaptcha api call returned with error-codes: #{@_recaptcha_reply.error_codes}."
|
|
41
|
+
else
|
|
42
|
+
"Recaptcha failure after api call. Api reply: #{@_recaptcha_reply}."
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
29
46
|
success
|
|
30
47
|
end
|
|
31
48
|
|
|
32
49
|
if verified
|
|
50
|
+
@_recaptcha_failure_reason = nil
|
|
33
51
|
flash.delete(:recaptcha_error) if recaptcha_flash_supported? && !model
|
|
34
52
|
true
|
|
35
53
|
else
|
|
@@ -41,6 +59,7 @@ module Recaptcha
|
|
|
41
59
|
false
|
|
42
60
|
end
|
|
43
61
|
rescue Timeout::Error
|
|
62
|
+
@_recaptcha_failure_reason = "Recaptcha server unreachable."
|
|
44
63
|
if Recaptcha.configuration.handle_timeouts_gracefully
|
|
45
64
|
recaptcha_error(
|
|
46
65
|
model,
|
|
@@ -57,13 +76,17 @@ module Recaptcha
|
|
|
57
76
|
end
|
|
58
77
|
|
|
59
78
|
def verify_recaptcha!(options = {})
|
|
60
|
-
verify_recaptcha(options) || raise(VerifyError)
|
|
79
|
+
verify_recaptcha(options) || raise(VerifyError, @_recaptcha_failure_reason)
|
|
61
80
|
end
|
|
62
81
|
|
|
63
82
|
def recaptcha_reply
|
|
64
83
|
@_recaptcha_reply if defined?(@_recaptcha_reply)
|
|
65
84
|
end
|
|
66
85
|
|
|
86
|
+
def recaptcha_failure_reason
|
|
87
|
+
@_recaptcha_failure_reason
|
|
88
|
+
end
|
|
89
|
+
|
|
67
90
|
def recaptcha_error(model, attribute, message)
|
|
68
91
|
if model
|
|
69
92
|
model.errors.add(attribute, message)
|
|
@@ -73,7 +96,9 @@ module Recaptcha
|
|
|
73
96
|
end
|
|
74
97
|
|
|
75
98
|
def recaptcha_flash_supported?
|
|
76
|
-
request.respond_to?(:format) &&
|
|
99
|
+
request.respond_to?(:format) && respond_to?(:flash) && (
|
|
100
|
+
request.format == :html || request.format == :turbo_stream
|
|
101
|
+
)
|
|
77
102
|
end
|
|
78
103
|
|
|
79
104
|
# Extracts response token from params. params['g-recaptcha-response-data'] for recaptcha_v3 or
|
|
@@ -37,8 +37,10 @@ module Recaptcha
|
|
|
37
37
|
'enterprise_verify_url' => 'https://recaptchaenterprise.googleapis.com/v1/projects'
|
|
38
38
|
}.freeze
|
|
39
39
|
|
|
40
|
-
attr_accessor
|
|
41
|
-
|
|
40
|
+
attr_accessor(
|
|
41
|
+
:default_env, :skip_verify_env, :proxy, :secret_key, :site_key, :handle_timeouts_gracefully,
|
|
42
|
+
:hostname, :enterprise, :enterprise_api_key, :enterprise_project_id, :response_limit, :response_minimum
|
|
43
|
+
)
|
|
42
44
|
attr_writer :api_server_url, :verify_url
|
|
43
45
|
|
|
44
46
|
def initialize # :nodoc:
|
|
@@ -57,6 +59,7 @@ module Recaptcha
|
|
|
57
59
|
@api_server_url = nil
|
|
58
60
|
|
|
59
61
|
@response_limit = 4000
|
|
62
|
+
@response_minimum = 100
|
|
60
63
|
end
|
|
61
64
|
|
|
62
65
|
def secret_key!
|
data/lib/recaptcha/helpers.rb
CHANGED
|
@@ -12,10 +12,11 @@ module Recaptcha
|
|
|
12
12
|
action = options.delete(:action) || raise(Recaptcha::RecaptchaError, 'action is required')
|
|
13
13
|
id = options.delete(:id) || "g-recaptcha-response-data-#{dasherize_action(action)}"
|
|
14
14
|
name = options.delete(:name) || "g-recaptcha-response-data[#{action}]"
|
|
15
|
-
|
|
15
|
+
turbo = options.delete(:turbo) || options.delete(:turbolinks)
|
|
16
16
|
options[:render] = site_key
|
|
17
17
|
options[:script_async] ||= false
|
|
18
18
|
options[:script_defer] ||= false
|
|
19
|
+
options[:ignore_no_element] = options.key?(:ignore_no_element) ? options[:ignore_no_element] : true
|
|
19
20
|
element = options.delete(:element)
|
|
20
21
|
element = element == false ? false : :input
|
|
21
22
|
if element == :input
|
|
@@ -23,11 +24,11 @@ module Recaptcha
|
|
|
23
24
|
end
|
|
24
25
|
options[:class] = "g-recaptcha-response #{options[:class]}"
|
|
25
26
|
|
|
26
|
-
if
|
|
27
|
+
if turbo
|
|
27
28
|
options[:onload] = recaptcha_v3_execute_function_name(action)
|
|
28
29
|
end
|
|
29
30
|
html, tag_attributes = components(options)
|
|
30
|
-
if
|
|
31
|
+
if turbo
|
|
31
32
|
html << recaptcha_v3_onload_script(site_key, action, callback, id, options)
|
|
32
33
|
elsif recaptcha_v3_inline_script?(options)
|
|
33
34
|
html << recaptcha_v3_inline_script(site_key, action, callback, id, options)
|
|
@@ -73,7 +74,7 @@ module Recaptcha
|
|
|
73
74
|
<div style="width: 300px; height: 60px; border-style: none;
|
|
74
75
|
bottom: 12px; left: 25px; margin: 0px; padding: 0px; right: 25px;
|
|
75
76
|
background: #f9f9f9; border: 1px solid #c1c1c1; border-radius: 3px;">
|
|
76
|
-
<textarea
|
|
77
|
+
<textarea name="g-recaptcha-response"
|
|
77
78
|
class="g-recaptcha-response"
|
|
78
79
|
style="width: 250px; height: 40px; border: 1px solid #c1c1c1;
|
|
79
80
|
margin: 10px 25px; padding: 0px; resize: none;">
|
|
@@ -138,6 +139,8 @@ module Recaptcha
|
|
|
138
139
|
nonce = options.delete(:nonce)
|
|
139
140
|
skip_script = (options.delete(:script) == false) || (options.delete(:external_script) == false)
|
|
140
141
|
ui = options.delete(:ui)
|
|
142
|
+
options.delete(:ignore_no_element)
|
|
143
|
+
options.delete(:inline_script)
|
|
141
144
|
|
|
142
145
|
data_attribute_keys = [:badge, :theme, :type, :callback, :expired_callback, :error_callback, :size]
|
|
143
146
|
data_attribute_keys << :tabindex unless ui == :button
|
|
@@ -206,7 +209,7 @@ module Recaptcha
|
|
|
206
209
|
})
|
|
207
210
|
};
|
|
208
211
|
|
|
209
|
-
#{recaptcha_v3_define_default_callback(callback) if recaptcha_v3_define_default_callback?(callback, action, options)}
|
|
212
|
+
#{recaptcha_v3_define_default_callback(callback, options) if recaptcha_v3_define_default_callback?(callback, action, options)}
|
|
210
213
|
</script>
|
|
211
214
|
HTML
|
|
212
215
|
end
|
|
@@ -224,7 +227,7 @@ module Recaptcha
|
|
|
224
227
|
});
|
|
225
228
|
});
|
|
226
229
|
};
|
|
227
|
-
#{recaptcha_v3_define_default_callback(callback) if recaptcha_v3_define_default_callback?(callback, action, options)}
|
|
230
|
+
#{recaptcha_v3_define_default_callback(callback, options) if recaptcha_v3_define_default_callback?(callback, action, options)}
|
|
228
231
|
</script>
|
|
229
232
|
HTML
|
|
230
233
|
end
|
|
@@ -235,12 +238,12 @@ module Recaptcha
|
|
|
235
238
|
options[:inline_script] != false
|
|
236
239
|
end
|
|
237
240
|
|
|
238
|
-
private_class_method def self.recaptcha_v3_define_default_callback(callback)
|
|
241
|
+
private_class_method def self.recaptcha_v3_define_default_callback(callback, options)
|
|
239
242
|
<<-HTML
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
243
|
+
var #{callback} = function(id, token) {
|
|
244
|
+
var element = document.getElementById(id);
|
|
245
|
+
#{element_check_condition(options)} element.value = token;
|
|
246
|
+
}
|
|
244
247
|
HTML
|
|
245
248
|
end
|
|
246
249
|
|
|
@@ -328,5 +331,9 @@ module Recaptcha
|
|
|
328
331
|
private_class_method def self.hash_to_query(hash)
|
|
329
332
|
hash.delete_if { |_, val| val.nil? || val.empty? }.to_a.map { |pair| pair.join('=') }.join('&')
|
|
330
333
|
end
|
|
334
|
+
|
|
335
|
+
private_class_method def self.element_check_condition(options)
|
|
336
|
+
options[:ignore_no_element] ? "if (element !== null)" : ""
|
|
337
|
+
end
|
|
331
338
|
end
|
|
332
339
|
end
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Recaptcha
|
|
4
|
+
class Reply
|
|
5
|
+
def initialize(raw_reply, enterprise:)
|
|
6
|
+
@raw_reply = raw_reply
|
|
7
|
+
@enterprise = enterprise
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def [](key)
|
|
11
|
+
@raw_reply[key.to_s]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def success?(options = {})
|
|
15
|
+
success.to_s == 'true' &&
|
|
16
|
+
hostname_valid?(options[:hostname]) &&
|
|
17
|
+
action_valid?(options[:action]) &&
|
|
18
|
+
score_above_threshold?(options[:minimum_score]) &&
|
|
19
|
+
score_below_threshold?(options[:maximum_score])
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def token_properties
|
|
23
|
+
@raw_reply['tokenProperties'] if enterprise?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def success
|
|
27
|
+
if enterprise?
|
|
28
|
+
token_properties&.dig('valid')
|
|
29
|
+
else
|
|
30
|
+
@raw_reply['success']
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def hostname
|
|
35
|
+
if enterprise?
|
|
36
|
+
token_properties&.dig('hostname')
|
|
37
|
+
else
|
|
38
|
+
@raw_reply['hostname']
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def action
|
|
43
|
+
if enterprise?
|
|
44
|
+
token_properties&.dig('action')
|
|
45
|
+
else
|
|
46
|
+
@raw_reply['action']
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def score
|
|
51
|
+
if enterprise?
|
|
52
|
+
@raw_reply.dig('riskAnalysis', 'score')
|
|
53
|
+
else
|
|
54
|
+
@raw_reply['score'] unless enterprise?
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def error_codes
|
|
59
|
+
if enterprise?
|
|
60
|
+
[]
|
|
61
|
+
else
|
|
62
|
+
@raw_reply['error-codes'] || []
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def challenge_ts
|
|
67
|
+
return @raw_reply['challenge_ts'] unless enterprise?
|
|
68
|
+
|
|
69
|
+
token_properties&.dig('createTime')
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def hostname_valid?(validation)
|
|
73
|
+
validation ||= Recaptcha.configuration.hostname
|
|
74
|
+
|
|
75
|
+
case validation
|
|
76
|
+
when nil, FalseClass
|
|
77
|
+
true
|
|
78
|
+
when String
|
|
79
|
+
validation == hostname
|
|
80
|
+
else
|
|
81
|
+
validation.call(hostname)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def action_valid?(expected_action)
|
|
86
|
+
case expected_action
|
|
87
|
+
when nil, FalseClass
|
|
88
|
+
true
|
|
89
|
+
else
|
|
90
|
+
action == expected_action.to_s
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def score_above_threshold?(minimum_score)
|
|
95
|
+
!minimum_score || (score && score >= minimum_score)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def score_below_threshold?(maximum_score)
|
|
99
|
+
!maximum_score || (score && score <= maximum_score)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def enterprise?
|
|
103
|
+
@enterprise
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def to_h
|
|
107
|
+
@raw_reply
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def to_s
|
|
111
|
+
@raw_reply.to_s
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def to_json(*args)
|
|
115
|
+
@raw_reply.to_json(*args)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
data/lib/recaptcha/version.rb
CHANGED
data/lib/recaptcha.rb
CHANGED
|
@@ -6,6 +6,7 @@ require 'uri'
|
|
|
6
6
|
|
|
7
7
|
require 'recaptcha/configuration'
|
|
8
8
|
require 'recaptcha/helpers'
|
|
9
|
+
require 'recaptcha/reply'
|
|
9
10
|
require 'recaptcha/adapters/controller_methods'
|
|
10
11
|
require 'recaptcha/adapters/view_methods'
|
|
11
12
|
if defined?(Rails)
|
|
@@ -51,11 +52,13 @@ module Recaptcha
|
|
|
51
52
|
end
|
|
52
53
|
|
|
53
54
|
def self.skip_env?(env)
|
|
54
|
-
|
|
55
|
+
resolved = env || configuration.default_env ||
|
|
56
|
+
(defined?(Rails.env) && Rails.env.to_s) # default_env is sometimes not set during initialization
|
|
57
|
+
configuration.skip_verify_env.include?(resolved)
|
|
55
58
|
end
|
|
56
59
|
|
|
57
60
|
def self.invalid_response?(resp)
|
|
58
|
-
resp.empty? || resp.length > configuration.response_limit
|
|
61
|
+
resp.empty? || resp.length > configuration.response_limit || resp.length < configuration.response_minimum
|
|
59
62
|
end
|
|
60
63
|
|
|
61
64
|
def self.verify_via_api_call(response, options)
|
|
@@ -76,21 +79,10 @@ module Recaptcha
|
|
|
76
79
|
body['event']['expectedAction'] = options[:action] if options.key?(:action)
|
|
77
80
|
body['event']['userIpAddress'] = options[:remote_ip] if options.key?(:remote_ip)
|
|
78
81
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
token_properties['valid'].to_s == 'true' &&
|
|
84
|
-
hostname_valid?(token_properties['hostname'], options[:hostname]) &&
|
|
85
|
-
action_valid?(token_properties['action'], options[:action]) &&
|
|
86
|
-
score_above_threshold?(score, options[:minimum_score]) &&
|
|
87
|
-
score_below_threshold?(score, options[:maximum_score])
|
|
88
|
-
|
|
89
|
-
if options[:with_reply] == true
|
|
90
|
-
[success, reply]
|
|
91
|
-
else
|
|
92
|
-
success
|
|
93
|
-
end
|
|
82
|
+
raw_reply = api_verification_enterprise(query_params, body, project_id, timeout: options[:timeout])
|
|
83
|
+
reply = Reply.new(raw_reply, enterprise: true)
|
|
84
|
+
result = reply.success?(options)
|
|
85
|
+
options[:with_reply] == true ? [result, reply] : result
|
|
94
86
|
end
|
|
95
87
|
|
|
96
88
|
def self.verify_via_api_call_free(response, options)
|
|
@@ -98,43 +90,10 @@ module Recaptcha
|
|
|
98
90
|
verify_hash = { 'secret' => secret_key, 'response' => response }
|
|
99
91
|
verify_hash['remoteip'] = options[:remote_ip] if options.key?(:remote_ip)
|
|
100
92
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
score_above_threshold?(reply['score'], options[:minimum_score]) &&
|
|
106
|
-
score_below_threshold?(reply['score'], options[:maximum_score])
|
|
107
|
-
|
|
108
|
-
if options[:with_reply] == true
|
|
109
|
-
[success, reply]
|
|
110
|
-
else
|
|
111
|
-
success
|
|
112
|
-
end
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
def self.hostname_valid?(hostname, validation)
|
|
116
|
-
validation ||= configuration.hostname
|
|
117
|
-
|
|
118
|
-
case validation
|
|
119
|
-
when nil, FalseClass then true
|
|
120
|
-
when String then validation == hostname
|
|
121
|
-
else validation.call(hostname)
|
|
122
|
-
end
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
def self.action_valid?(action, expected_action)
|
|
126
|
-
case expected_action
|
|
127
|
-
when nil, FalseClass then true
|
|
128
|
-
else action == expected_action
|
|
129
|
-
end
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
def self.score_above_threshold?(score, minimum_score)
|
|
133
|
-
!minimum_score || (score && score >= minimum_score)
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
def self.score_below_threshold?(score, maximum_score)
|
|
137
|
-
!maximum_score || (score && score <= maximum_score)
|
|
93
|
+
raw_reply = api_verification_free(verify_hash, timeout: options[:timeout], json: options[:json])
|
|
94
|
+
reply = Reply.new(raw_reply, enterprise: false)
|
|
95
|
+
result = reply.success?(options)
|
|
96
|
+
options[:with_reply] == true ? [result, reply] : result
|
|
138
97
|
end
|
|
139
98
|
|
|
140
99
|
def self.http_client_for(uri:, timeout: nil)
|
|
@@ -152,11 +111,18 @@ module Recaptcha
|
|
|
152
111
|
instance
|
|
153
112
|
end
|
|
154
113
|
|
|
155
|
-
def self.api_verification_free(verify_hash, timeout: nil)
|
|
156
|
-
|
|
157
|
-
|
|
114
|
+
def self.api_verification_free(verify_hash, timeout: nil, json: false)
|
|
115
|
+
if json
|
|
116
|
+
uri = URI.parse(configuration.verify_url)
|
|
117
|
+
request = Net::HTTP::Post.new(uri.request_uri)
|
|
118
|
+
request['Content-Type'] = 'application/json; charset=utf-8'
|
|
119
|
+
request.body = JSON.generate(verify_hash)
|
|
120
|
+
else
|
|
121
|
+
query = URI.encode_www_form(verify_hash)
|
|
122
|
+
uri = URI.parse("#{configuration.verify_url}?#{query}")
|
|
123
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
|
124
|
+
end
|
|
158
125
|
http_instance = http_client_for(uri: uri, timeout: timeout)
|
|
159
|
-
request = Net::HTTP::Get.new(uri.request_uri)
|
|
160
126
|
JSON.parse(http_instance.request(request).body)
|
|
161
127
|
end
|
|
162
128
|
|
|
@@ -165,6 +131,7 @@ module Recaptcha
|
|
|
165
131
|
uri = URI.parse("#{configuration.verify_url}/#{project_id}/assessments?#{query}")
|
|
166
132
|
http_instance = http_client_for(uri: uri, timeout: timeout)
|
|
167
133
|
request = Net::HTTP::Post.new(uri.request_uri)
|
|
134
|
+
request['Referer'] = Rails.application.default_url_options[:host] if defined? Rails.application
|
|
168
135
|
request['Content-Type'] = 'application/json; charset=utf-8'
|
|
169
136
|
request.body = JSON.generate(body)
|
|
170
137
|
JSON.parse(http_instance.request(request).body)
|
metadata
CHANGED
|
@@ -1,29 +1,15 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: recaptcha
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.
|
|
4
|
+
version: 5.21.2
|
|
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: 2026-04-09 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
|
-
- !ruby/object:Gem::Dependency
|
|
14
|
-
name: json
|
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
|
16
|
-
requirements:
|
|
17
|
-
- - ">="
|
|
18
|
-
- !ruby/object:Gem::Version
|
|
19
|
-
version: '0'
|
|
20
|
-
type: :runtime
|
|
21
|
-
prerelease: false
|
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
-
requirements:
|
|
24
|
-
- - ">="
|
|
25
|
-
- !ruby/object:Gem::Version
|
|
26
|
-
version: '0'
|
|
27
13
|
- !ruby/object:Gem::Dependency
|
|
28
14
|
name: mocha
|
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -153,16 +139,23 @@ files:
|
|
|
153
139
|
- lib/recaptcha/helpers.rb
|
|
154
140
|
- lib/recaptcha/rails.rb
|
|
155
141
|
- lib/recaptcha/railtie.rb
|
|
142
|
+
- lib/recaptcha/reply.rb
|
|
156
143
|
- lib/recaptcha/version.rb
|
|
144
|
+
- rails/locales/de.yml
|
|
157
145
|
- rails/locales/en.yml
|
|
146
|
+
- rails/locales/es.yml
|
|
158
147
|
- rails/locales/fr.yml
|
|
148
|
+
- rails/locales/it.yml
|
|
159
149
|
- rails/locales/ja.yml
|
|
160
150
|
- rails/locales/nl.yml
|
|
151
|
+
- rails/locales/pt-BR.yml
|
|
152
|
+
- rails/locales/pt.yml
|
|
161
153
|
homepage: http://github.com/ambethia/recaptcha
|
|
162
154
|
licenses:
|
|
163
155
|
- MIT
|
|
164
156
|
metadata:
|
|
165
157
|
source_code_uri: https://github.com/ambethia/recaptcha
|
|
158
|
+
rubygems_mfa_required: 'true'
|
|
166
159
|
post_install_message:
|
|
167
160
|
rdoc_options: []
|
|
168
161
|
require_paths:
|
|
@@ -171,14 +164,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
171
164
|
requirements:
|
|
172
165
|
- - ">="
|
|
173
166
|
- !ruby/object:Gem::Version
|
|
174
|
-
version:
|
|
167
|
+
version: 3.0.0
|
|
175
168
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
176
169
|
requirements:
|
|
177
170
|
- - ">="
|
|
178
171
|
- !ruby/object:Gem::Version
|
|
179
172
|
version: '0'
|
|
180
173
|
requirements: []
|
|
181
|
-
rubygems_version: 3.
|
|
174
|
+
rubygems_version: 3.4.10
|
|
182
175
|
signing_key:
|
|
183
176
|
specification_version: 4
|
|
184
177
|
summary: Helpers for the reCAPTCHA API
|