Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ status: true
dependencies: { }
id: recaptcha3
label: reCaptcha3
threshold: 0.7
threshold: 0.5
challenge: image_captcha/Image
7 changes: 7 additions & 0 deletions web/modules/custom/do_base/do_base.libraries.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
recaptcha_v3_guard:
js:
js/do_base.recaptcha_v3_guard.js: {}
dependencies:
- core/drupal
- core/once

highlight_js.gherkin:
js:
https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/gherkin.min.js: { type: external, minified: true }
Expand Down
68 changes: 68 additions & 0 deletions web/modules/custom/do_base/js/do_base.recaptcha_v3_guard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* @file
* Prevents form submission until the reCAPTCHA v3 token is populated.
* @param {Object} Drupal - The Drupal object.
* @param {Function} once - The once function.
*/

(function doBaseRecaptchaV3Guard(Drupal, once) {
/**
* Enables all submit buttons in a form.
*
* @param {NodeList} submits - The submit buttons to enable.
*/
function enableSubmits(submits) {
submits.forEach(function enableBtn(btn) {
btn.disabled = false;
});
}

/**
* Watches a token input and enables submit buttons when the token is set.
*
* @param {HTMLElement} tokenInput - The reCAPTCHA token hidden input.
* @param {NodeList} submits - The submit buttons to control.
*/
function watchToken(tokenInput, submits) {
// Poll for the token value since hidden input value changes do not
// reliably fire DOM events.
const poll = setInterval(function checkToken() {
if (tokenInput.value) {
clearInterval(poll);
enableSubmits(submits);
}
}, 200);

// Safety timeout: re-enable buttons after 5 seconds regardless so the
// form is never permanently locked.
setTimeout(function safetyTimeout() {
clearInterval(poll);
enableSubmits(submits);
}, 5000);
}

Drupal.behaviors.doBaseRecaptchaV3Guard = {
attach: function attachGuard(context) {
once('recaptcha-v3-guard', '.recaptcha-v3-token', context).forEach(
function processToken(tokenInput) {
const form = tokenInput.closest('form');
if (!form) {
return;
}

const submits = form.querySelectorAll('[type="submit"]');
if (!submits.length) {
return;
}

// Disable submit buttons until the token is ready.
submits.forEach(function disableBtn(btn) {
btn.disabled = true;
});

watchToken(tokenInput, submits);
},
);
},
};
})(Drupal, once);
32 changes: 32 additions & 0 deletions web/modules/custom/do_base/src/Hook/FormAlterHook.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace Drupal\do_base\Hook;

use Drupal\Core\Hook\Attribute\Hook;

/**
* Form alter hooks for do_base module.
*/
final class FormAlterHook {

/**
* Implements hook_form_alter().
*
* Attaches the reCAPTCHA v3 guard library to forms that use reCAPTCHA v3
* to prevent submission before the token is populated.
*/
#[Hook('form_alter')]
public function alter(array &$form): void {
if (!isset($form['captcha'])) {
return;
}

$captcha_type = $form['captcha']['#captcha_type'] ?? '';
if (str_contains($captcha_type, 'recaptcha_v3')) {
$form['#attached']['library'][] = 'do_base/recaptcha_v3_guard';
}
}

}
10 changes: 10 additions & 0 deletions web/themes/custom/drevops/drevops.theme
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ function drevops_preprocess_container(array &$variables): void {
$variables['modifier_class'] = FALSE;
}

/**
* Implements hook_preprocess_HOOK() for hidden input templates.
*/
function drevops_preprocess_input__hidden(array &$variables): void {
// Hidden inputs should not have classes as classes are used for presentation.
// But some contributed modules use classes on hidden inputs and target them
// with JavaScript, so we need to allow classes if they are already set.
$variables['modifier_class'] = FALSE;
}
Comment on lines +28 to +36
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Preserve existing modifier_class values instead of always overwriting (Line 35).
The comment says to allow existing classes, but Line 35 always forces modifier_class to FALSE, which can wipe module-provided modifier classes. Consider only forcing FALSE when no value is present.

🛠️ Suggested fix
 function drevops_preprocess_input__hidden(array &$variables): void {
   // Hidden inputs should not have classes as classes are used for presentation.
   // But some contributed modules use classes on hidden inputs and target them
   // with JavaScript, so we need to allow classes if they are already set.
-  $variables['modifier_class'] = FALSE;
+  if (empty($variables['modifier_class'])) {
+    $variables['modifier_class'] = FALSE;
+  }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* Implements hook_preprocess_HOOK() for hidden input templates.
*/
function drevops_preprocess_input__hidden(array &$variables): void {
// Hidden inputs should not have classes as classes are used for presentation.
// But some contributed modules use classes on hidden inputs and target them
// with JavaScript, so we need to allow classes if they are already set.
$variables['modifier_class'] = FALSE;
}
/**
* Implements hook_preprocess_HOOK() for hidden input templates.
*/
function drevops_preprocess_input__hidden(array &$variables): void {
// Hidden inputs should not have classes as classes are used for presentation.
// But some contributed modules use classes on hidden inputs and target them
// with JavaScript, so we need to allow classes if they are already set.
if (empty($variables['modifier_class'])) {
$variables['modifier_class'] = FALSE;
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/themes/custom/drevops/drevops.theme` around lines 28 - 36, The
drevops_preprocess_input__hidden function currently unconditionally sets
$variables['modifier_class'] = FALSE which wipes any existing modifier classes;
change it so you only set it to FALSE when the key is missing or empty (e.g.,
check isset() or empty() on $variables['modifier_class']) and otherwise preserve
the existing value so contributed modules' classes remain intact.


/**
* Implements hook_preprocess_HOOK().
*/
Expand Down
Loading