From b8f94b634a42ff9e8b40fa239276870b72f4fc28 Mon Sep 17 00:00:00 2001 From: Ivan Bochkarev Date: Thu, 23 Jul 2026 12:14:34 +0600 Subject: [PATCH] fix: surface silently skipped custom validators in the log When a &validate entry names a type that isn't a built-in method and isn't listed in customValidators, Validator::validate() skips it and treats the field as valid with no error message. That's by design (customValidators is an allowlist to stop brute-forcing arbitrary snippets), but the only trace was a LOG_LEVEL_INFO log entry, which sits below MODX's default log_level and never shows up. From the outside this looks exactly like "the custom validator's error message isn't showing", including for validators like reCAPTCHA that run through the same customValidators-gated snippet path. Bump that log line to LOG_LEVEL_WARN and spell out the fix (add the type to &customValidators) so the actual cause is visible without digging into FormIt's source. Also hardened the customValidators normalization above it: $this->config['customValidators'] can arrive as an array (the constructor already explodes it from formit->config) or as a raw string (when the config passed to the Validator's constructor carries the unprocessed scriptProperty). The old code always called explode() on it, which throws a TypeError if it's already an array. Reproduced both paths with a standalone script exercising Validator::validateFields()/validate() against a stub custom validator snippet. Fixes #273 --- core/components/formit/src/FormIt/Validator.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/core/components/formit/src/FormIt/Validator.php b/core/components/formit/src/FormIt/Validator.php index 21f96b7..5f16003 100644 --- a/core/components/formit/src/FormIt/Validator.php +++ b/core/components/formit/src/FormIt/Validator.php @@ -252,8 +252,10 @@ public function validate($key, $value, $type = '') /** @var array $invNames An array of invalid hook names to skip */ $invNames = array('validate','validateFields','addError','__construct'); - $customValidators = !empty($this->config['customValidators']) ? $this->config['customValidators'] : ''; - $customValidators = explode(',',$customValidators); + $customValidators = !empty($this->config['customValidators']) ? $this->config['customValidators'] : array(); + if (!is_array($customValidators)) { + $customValidators = explode(',',$customValidators); + } if (method_exists($this,$type) && !in_array($type,$invNames)) { /* built-in validator */ $validated = $this->$type($key,$value,$param); @@ -280,7 +282,10 @@ public function validate($key, $value, $type = '') $validated = true; } } else { - $this->modx->log(\modX::LOG_LEVEL_INFO,'[FormIt] Validator "'.$type.'" for field "'.$key.'" was not specified in the customValidators property.'); + /* not allowlisted: skip the validator and treat the field as valid, but warn loudly + since LOG_LEVEL_INFO is below MODX's default log_level and would otherwise stay + invisible, making the missing error message look like a bug in FormIt itself */ + $this->modx->log(\modX::LOG_LEVEL_WARN,'[FormIt] Validator "'.$type.'" for field "'.$key.'" was skipped because it is not listed in the customValidators property. Add it there (e.g. &customValidators=`'.$type.'`) if it should run.'); $validated = true; }