Version 1.4.10 - security hardening, empty fallback, and reCAPTCHA improvements
- Harden XSS protection with escapeHtml on all rendered output values - Add empty_fallback support for empty cell display across all view types - Fix reCAPTCHA default action to 'submit' matching JS side - Move reCAPTCHA token generation from inline PHP to public.js - Lower default reCAPTCHA min score from 0.5 to 0.3 - Improve reCAPTCHA token age check and preload error handling - Add form submit handler for enter key support - Increase waitForRecaptcha timeout to 10 seconds - Show button/color settings only for button output types - Remove debug console.log and error_log statements - Bump version to 1.4.10
This commit is contained in:
@@ -127,7 +127,7 @@ class CHECKER_SECURITY {
|
||||
$secret_key_raw = isset($checker['security']['recaptcha']['secret_key']) ? $checker['security']['recaptcha']['secret_key'] : '';
|
||||
$secret_key = trim((string) $secret_key_raw);
|
||||
|
||||
$min_score_raw = isset($checker['security']['recaptcha']['min_score']) ? $checker['security']['recaptcha']['min_score'] : 0.5;
|
||||
$min_score_raw = isset($checker['security']['recaptcha']['min_score']) ? $checker['security']['recaptcha']['min_score'] : 0.3;
|
||||
if (is_string($min_score_raw)) {
|
||||
$min_score_raw = str_replace(',', '.', $min_score_raw);
|
||||
}
|
||||
@@ -199,15 +199,12 @@ class CHECKER_SECURITY {
|
||||
}
|
||||
|
||||
if ($score < $min_score) {
|
||||
error_log("Sheet Data Checker: reCAPTCHA score too low - Score: {$score}, Min: {$min_score}");
|
||||
return [
|
||||
'success' => false,
|
||||
'score' => $score,
|
||||
'message' => 'reCAPTCHA score too low. Please try again.'
|
||||
'message' => "reCAPTCHA score too low ({$score}). Please try again."
|
||||
];
|
||||
}
|
||||
|
||||
error_log("Sheet Data Checker: reCAPTCHA verification SUCCESS - Score: {$score}, Action: {$response_action}");
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
|
||||
@@ -504,6 +504,7 @@ class SHEET_DATA_CHECKER_PRO
|
||||
'type' => isset($checker['output'][$id]['type']) ? $checker['output'][$id]['type'] : 'text',
|
||||
'button_text' => isset($checker['output'][$id]['button_text']) ? $checker['output'][$id]['button_text'] : '',
|
||||
'prefix' => isset($checker['output'][$id]['prefix']) ? $checker['output'][$id]['prefix'] : '',
|
||||
'empty_fallback' => isset($checker['output'][$id]['empty_fallback']) ? $checker['output'][$id]['empty_fallback'] : '',
|
||||
'bg_color' => isset($checker['output'][$id]['bg_color']) ? $checker['output'][$id]['bg_color'] : '#cccccc',
|
||||
'text_color' => isset($checker['output'][$id]['text_color']) ? $checker['output'][$id]['text_color'] : '#000000',
|
||||
'display' => isset($checker['result']['display']) && $checker['result']['display'] == 'card'
|
||||
|
||||
@@ -60,7 +60,8 @@ class CHECKER_CAPTCHA_HELPER {
|
||||
}
|
||||
} elseif ($recaptcha_enabled) {
|
||||
$site_key = $checker['security']['recaptcha']['site_key'] ?? '';
|
||||
$action = $checker['security']['recaptcha']['action'] ?? 'checker_validate';
|
||||
// Default action must match JavaScript default ('submit') to avoid score penalties
|
||||
$action = $checker['security']['recaptcha']['action'] ?? 'submit';
|
||||
$hide_badge = $checker['security']['recaptcha']['hide_badge'] ?? 'no';
|
||||
|
||||
if ($site_key) {
|
||||
@@ -87,101 +88,31 @@ class CHECKER_CAPTCHA_HELPER {
|
||||
true
|
||||
);
|
||||
|
||||
// Pass settings to JavaScript
|
||||
// Pass settings to JavaScript - let public.js handle token generation
|
||||
wp_add_inline_script(
|
||||
'google-recaptcha-v3',
|
||||
'
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
console.log("[CAPTCHA Debug] DOMContentLoaded - checking for grecaptcha...");
|
||||
console.log("[CAPTCHA Debug] grecaptcha available:", typeof grecaptcha !== "undefined");
|
||||
|
||||
if (typeof grecaptcha !== "undefined") {
|
||||
console.log("[CAPTCHA Debug] reCAPTCHA v3 detected, initializing...");
|
||||
|
||||
// Store reCAPTCHA settings globally
|
||||
// Store reCAPTCHA settings globally for public.js to use
|
||||
window.checkerRecaptcha = {
|
||||
siteKey: "' . esc_js($site_key) . '",
|
||||
action: "' . esc_js($action) . '"
|
||||
};
|
||||
|
||||
console.log("[CAPTCHA Debug] reCAPTCHA settings:", window.checkerRecaptcha);
|
||||
|
||||
' . ($hide_badge ? '
|
||||
// Hide reCAPTCHA badge
|
||||
var style = document.createElement("style");
|
||||
style.innerHTML = ".grecaptcha-badge { display: none !important; }";
|
||||
document.head.appendChild(style);
|
||||
console.log("[CAPTCHA Debug] reCAPTCHA badge hidden");
|
||||
' : '
|
||||
// Ensure badge is visible
|
||||
var style = document.createElement("style");
|
||||
style.innerHTML = ".grecaptcha-badge { visibility: visible !important; opacity: 1 !important; display: block !important; }";
|
||||
document.head.appendChild(style);
|
||||
') . '
|
||||
|
||||
// Initialize reCAPTCHA for all checker forms
|
||||
initRecaptchaForForms();
|
||||
console.log("[CAPTCHA Debug] reCAPTCHA initialization complete");
|
||||
} else {
|
||||
console.error("[CAPTCHA Debug] grecaptcha NOT available - script may not have loaded");
|
||||
}
|
||||
});
|
||||
|
||||
function initRecaptchaForForms() {
|
||||
var forms = document.querySelectorAll(".dw-checker-container form");
|
||||
forms.forEach(function(form) {
|
||||
var searchButton = form.querySelector(".search-button");
|
||||
if (!searchButton) return;
|
||||
|
||||
// Generate token when search button is clicked
|
||||
searchButton.addEventListener("click", function(e) {
|
||||
var tokenInput = form.querySelector("input[name=recaptcha_token]");
|
||||
|
||||
// If token already exists and is recent (less than 2 minutes old), don\'t regenerate
|
||||
if (tokenInput && tokenInput.value && tokenInput.dataset.timestamp) {
|
||||
var tokenAge = Date.now() - parseInt(tokenInput.dataset.timestamp);
|
||||
if (tokenAge < 120000) { // 2 minutes
|
||||
console.log("reCAPTCHA: Using existing token (age: " + Math.floor(tokenAge/1000) + "s)");
|
||||
return; // Let the click proceed with existing token
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent default to wait for token generation
|
||||
var hasToken = tokenInput && tokenInput.value;
|
||||
if (!hasToken) {
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
|
||||
console.log("reCAPTCHA: Generating new token...");
|
||||
|
||||
// Generate new token
|
||||
grecaptcha.ready(function() {
|
||||
grecaptcha.execute(
|
||||
window.checkerRecaptcha.siteKey,
|
||||
{action: window.checkerRecaptcha.action}
|
||||
).then(function(token) {
|
||||
console.log("reCAPTCHA: Token generated successfully");
|
||||
// Add or update token in hidden input
|
||||
if (!tokenInput) {
|
||||
tokenInput = document.createElement("input");
|
||||
tokenInput.type = "hidden";
|
||||
tokenInput.name = "recaptcha_token";
|
||||
form.appendChild(tokenInput);
|
||||
}
|
||||
tokenInput.value = token;
|
||||
tokenInput.dataset.timestamp = Date.now().toString();
|
||||
|
||||
// Trigger the search button click again
|
||||
searchButton.click();
|
||||
}).catch(function(error) {
|
||||
console.error("reCAPTCHA error:", error);
|
||||
alert("reCAPTCHA verification failed. Please refresh the page.");
|
||||
});
|
||||
});
|
||||
}
|
||||
}, true); // Use capture phase to run before other click handlers
|
||||
});
|
||||
}'
|
||||
});'
|
||||
);
|
||||
}
|
||||
|
||||
@@ -208,24 +139,16 @@ class CHECKER_CAPTCHA_HELPER {
|
||||
'cloudflare-turnstile',
|
||||
'
|
||||
window.onloadTurnstileCallback = function() {
|
||||
console.log("[CAPTCHA Debug] Turnstile callback triggered");
|
||||
|
||||
// Store Turnstile settings globally
|
||||
window.checkerTurnstile = {
|
||||
siteKey: "' . esc_js($site_key) . '",
|
||||
theme: "' . esc_js($theme) . '",
|
||||
size: "' . esc_js($size) . '"
|
||||
};
|
||||
|
||||
console.log("[CAPTCHA Debug] Turnstile settings:", window.checkerTurnstile);
|
||||
|
||||
// Initialize Turnstile for all checker forms
|
||||
initTurnstileForForms();
|
||||
console.log("[CAPTCHA Debug] Turnstile initialization complete");
|
||||
};
|
||||
|
||||
// Also log when script loads
|
||||
console.log("[CAPTCHA Debug] Turnstile script loading...");
|
||||
|
||||
function initTurnstileForForms() {
|
||||
var forms = document.querySelectorAll(".dw-checker-container form");
|
||||
@@ -244,7 +167,6 @@ class CHECKER_CAPTCHA_HELPER {
|
||||
}
|
||||
|
||||
// Render Turnstile widget
|
||||
console.log("Turnstile: Initializing widget");
|
||||
turnstile.render(container, {
|
||||
sitekey: window.checkerTurnstile.siteKey,
|
||||
theme: window.checkerTurnstile.theme,
|
||||
@@ -264,10 +186,10 @@ class CHECKER_CAPTCHA_HELPER {
|
||||
console.log("Turnstile: Token stored in form");
|
||||
},
|
||||
"error-callback": function(error) {
|
||||
console.error("Turnstile error:", error);
|
||||
// Silently handle errors
|
||||
},
|
||||
"expired-callback": function() {
|
||||
console.warn("Turnstile: Token expired, please complete challenge again");
|
||||
// Reset expired token
|
||||
var input = form.querySelector("input[name=turnstile_token]");
|
||||
if (input) {
|
||||
input.value = "";
|
||||
|
||||
Reference in New Issue
Block a user