The ConditionAwareValidator reads the session (tx_powermail_cond) to determine whether a field is hidden and should skip mandatory validation. This session is populated by the AJAX call in PowermailConditions.js on every field change.
If the AJAX call fails due to a brief connection interruption, the session retains a stale state. The validator then believes a field is hidden when it is should be actually visible, skips mandatory validation, and the form submits successfully with missing required fields.
Steps to reproduce
- Open a form with a conditional mandatory field (e.g. an email field shown only when "By email" is checked)
- Fill out the form without checking the conditional option
- Trigger a brief connection interruption (unstable Wi-Fi, mobile network handoff, VPN reconnect, etc.)
- While disconnected, check the conditional option — the AJAX call fails silently
- Connection resumes
- Submit the form
Result: Form submits without the mandatory email field.
Expected: Server-side validation rejects the submission.
Root cause
ConditionAwareValidator::isValidFieldInMandatoryValidation() trusts the session blindly:
$arguments = $GLOBALS['TYPO3_REQUEST']->getAttribute('frontend.user')->getSessionData('tx_powermail_cond');
If the AJAX call never updated the session (connection lost at that moment), the validator uses stale data.
Suggested fix
Recalculate conditions from the submitted POST data at validation time, instead of relying on the session. The POST contains the same tx_powermail_pi1 payload that the AJAX call would have sent:
protected function isValidFieldInMandatoryValidation(Field $field, $value): void
{
$parsedBody = $GLOBALS['TYPO3_REQUEST']->getParsedBody() ?? [];
$powermailArgs = $parsedBody['tx_powermail_pi1'] ?? [];
// Fallback to session if POST data is unavailable
if (empty($powermailArgs['field']) || empty($powermailArgs['mail']['form'])) {
parent::isValidFieldInMandatoryValidation($field, $value);
return;
}
// Recalculate conditions from submitted values
$conditionContainer = $this->conditionContainerRepository->findOneByForm((int)$powermailArgs['mail']['form']);
if ($conditionContainer === null) {
parent::isValidFieldInMandatoryValidation($field, $value);
return;
}
$form = $field->getPage()?->getForm();
// Hydrate fields with POST values
foreach ($form->getPages() as $page) {
foreach ($page->getFields() as $f) {
$fields[$f->getMarker()] = $f;
}
}
foreach ($powermailArgs['field'] as $name => $val) {
if (isset($fields[$name])) {
$fields[$name]->setText(is_array($val) ? json_encode($val) : (string)$val);
}
}
$arguments = $conditionContainer->applyConditions($form, $powermailArgs);
// Check if field is hidden
$formUid = $form->getUid();
$pageUid = $field->getPage()->getUid();
$action = $arguments[Condition::INDEX_TODO][$formUid][$pageUid][$field->getMarker()][Condition::INDEX_ACTION] ?? null;
if ($action === Condition::ACTION_HIDE_STRING) {
return;
}
// Field is visible — apply standard mandatory validation
if (in_array($field->getType(), $this->mandatoryValidationFieldTypes)
&& $field->isMandatory()
&& !$this->validateMandatory($value)
) {
$this->setErrorAndMessage($field, 'mandatory');
}
}
The ConditionAwareValidator reads the session (tx_powermail_cond) to determine whether a field is hidden and should skip mandatory validation. This session is populated by the AJAX call in PowermailConditions.js on every field change.
If the AJAX call fails due to a brief connection interruption, the session retains a stale state. The validator then believes a field is hidden when it is should be actually visible, skips mandatory validation, and the form submits successfully with missing required fields.
Steps to reproduce
Result: Form submits without the mandatory email field.
Expected: Server-side validation rejects the submission.
Root cause
ConditionAwareValidator::isValidFieldInMandatoryValidation() trusts the session blindly:
If the AJAX call never updated the session (connection lost at that moment), the validator uses stale data.
Suggested fix
Recalculate conditions from the submitted POST data at validation time, instead of relying on the session. The POST contains the same tx_powermail_pi1 payload that the AJAX call would have sent: