Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
= 5.4.1 =
* Improved Auto-Verification compatibility with forms on query-based page URLs and Brevo forms, while simplifying form registration storage.

= 5.4.0 =
* Added optional AJAX submission for standard WordPress comment forms, with inline error messages.
* Added form data processing for anti-spam checks in 10 popular integrations: WordPress Core, WooCommerce, Divi, Ultimate Addons for Elementor, Essential Addons, Mailchimp, Maintenance, Ultimate Member, bbPress, and GiveWP.
Expand Down
4 changes: 2 additions & 2 deletions hcaptcha.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* Plugin Name: hCaptcha for WP
* Plugin URI: https://www.hcaptcha.com/
* Description: hCaptcha keeps out bots and spam while putting privacy first. It is a drop-in replacement for reCAPTCHA.
* Version: 5.4.0
* Version: 5.4.1
* Requires at least: 6.0
* Requires PHP: 7.4
* Author: hCaptcha
Expand Down Expand Up @@ -39,7 +39,7 @@
/**
* Plugin version.
*/
const HCAPTCHA_VERSION = '5.4.0';
const HCAPTCHA_VERSION = '5.4.1';

/**
* Path to the plugin dir.
Expand Down
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,11 @@
},
"resolutions": {
"@babel/plugin-transform-modules-systemjs": "^7.29.7",
"brace-expansion@npm:^1": "^1.1.16",
"brace-expansion@npm:^2": "^2.1.2",
"brace-expansion@npm:^5": "^5.0.8",
"brace-expansion@npm:^1": "^1.1.21",
"brace-expansion@npm:^2": "^2.1.7",
"brace-expansion@npm:^5": "^5.0.12",
"cross-spawn": "^7.0.6",
"fast-uri": "^3.1.7",
"fast-uri": "^3.1.8",
"flatted": "^3.4.2",
"js-yaml": "^4.3.0",
"micromatch": "^4.0.8",
Expand All @@ -83,7 +83,7 @@
"picomatch@npm:^4": "^4.0.5",
"postcss": "^8.5.25",
"qs": "^6.15.3",
"serialize-javascript": "^7.0.6",
"serialize-javascript": "^7.1.2",
"svgo": "^4.1.0",
"tar": "^7.5.22",
"undici": "^7.29.0"
Expand Down
6 changes: 5 additions & 1 deletion readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Tags: captcha, hcaptcha, recaptcha, antispam, spam
Requires at least: 6.0
Tested up to: 7.1
Requires PHP: 7.4
Stable tag: 5.4.0
Stable tag: 5.4.1
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Expand Down Expand Up @@ -1053,6 +1053,10 @@ Instructions for popular native integrations are below:

== Changelog ==

= 5.4.1 =
* Fixed WordPress login authentication when login integrations have different protection settings.
* Improved Auto-Verification compatibility with forms on query-based page URLs and Brevo forms, while simplifying form registration storage.

= 5.4.0 =
* Added optional AJAX submission for standard WordPress comment forms, with inline error messages.
* Added form data processing for anti-spam checks in 10 popular integrations: WordPress Core, WooCommerce, Divi, Ultimate Addons for Elementor, Essential Addons, Mailchimp, Maintenance, Ultimate Member, bbPress, and GiveWP.
Expand Down
87 changes: 81 additions & 6 deletions src/php/Abstracts/LoginBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,17 @@ abstract class LoginBase {
*/
protected const NONCE = 'hcaptcha_login_nonce';

/**
* Settings for signed login form contexts.
*/
private const FORM_SETTINGS = [
'wp-login' => 'wp_status',
'elementor-login' => 'elementor_pro_status',
'divi-login' => 'divi_status',
'divi_builder-login' => 'divi_builder_status',
'extra-login' => 'extra_status',
];

/**
* Legacy login attempts the data option name.
*/
Expand Down Expand Up @@ -118,12 +129,15 @@ protected function init_hooks(): void {
/**
* Display signature.
*
* @param string $form_id Signed rendering context, independent of the login threshold.
*
* @return void
*/
public function display_signature(): void {
public function display_signature( string $form_id = 'login' ): void {
$this->login_form_shown = true;
$form_id = doing_action( 'login_form' ) && $this->is_wp_login_form() ? 'wp-login' : $form_id;

HCaptcha::display_signature( static::class, 'login', $this->hcaptcha_shown );
HCaptcha::display_signature( static::class, $form_id, $this->hcaptcha_shown );
}

/**
Expand Down Expand Up @@ -195,8 +209,53 @@ public function allow_wp_login_skip_verification( $can_skip ): bool {
* @return bool
*/
private function is_login_verification_owner(): bool {
return false !== has_filter( 'wp_authenticate_user', [ $this, 'check_signature' ] ) &&
null === HCaptcha::check_signature( static::class, 'login' );
[ $check, $enabled ] = $this->get_login_signature();

return $this->is_login_enabled() &&
false !== has_filter( 'wp_authenticate_user', [ $this, 'check_signature' ] ) &&
$enabled && null === $check;
}

/**
* Validate the rendering context and look up its current protection setting.
*
* Legacy signatures still require the current threshold. Only a valid signed
* context can select a deliberately disabled form; missing or tampered fields
* cannot turn a below-threshold signature into a permanent exemption.
*
* @return array{0: bool|null, 1: bool} Signature result and current protection setting.
*/
private function get_login_signature(): array {
$check = HCaptcha::check_signature( static::class, 'login' );

if ( false !== $check ) {
return [ $check, true ];
}

foreach ( self::FORM_SETTINGS as $form_id => $setting ) {
$check = HCaptcha::check_signature( static::class, $form_id );

if ( false !== $check ) {
$enabled = hcaptcha()->settings()->is( $setting, 'login' );

if ( 'wp-login' === $form_id ) {
/**
* Filters whether the signed native login form is protected.
*
* Integrations such as Wordfence can deliberately disable native
* hCaptcha protection while leaving the WordPress setting enabled.
* This filter applies only after the native context is validated.
*
* @param bool $enabled Whether native login protection is enabled.
*/
$enabled = (bool) apply_filters( 'hcap_wp_login_protection_enabled', $enabled );
}

return [ $check, $enabled ];
}
}

return [ false, true ];
}

/**
Expand All @@ -209,11 +268,15 @@ private function is_login_verification_owner(): bool {
* @noinspection PhpUnusedParameterInspection
*/
public function check_signature( $user, string $password ) {
if ( ! $this->is_wp_login_form() ) {
if ( ! $this->is_wp_login_form() || ! $this->is_login_enabled() ) {
return $user;
}

$check = HCaptcha::check_signature( static::class, 'login' );
[ $check, $enabled ] = $this->get_login_signature();

if ( ! $enabled ) {
return $user;
}

if ( $check && $this->can_skip_login_verification() ) {
return $user;
Expand All @@ -229,6 +292,18 @@ public function check_signature( $user, string $password ) {
return $this->login_base_verify( $user, $password );
}

/**
* Whether this integration's login protection is enabled.
*
* Most integrations are loaded only when enabled. Always-loaded integrations
* override this method so they cannot verify or own a disabled login form.
*
* @return bool
*/
protected function is_login_enabled(): bool {
return true;
}

/**
* Whether a valid signature can skip login verification.
*
Expand Down
Loading
Loading