Skip to content

Fix submit-event.php crash on non-numeric date fields - #1970

Open
svpernova09 wants to merge 1 commit into
php:masterfrom
svpernova09:submit-event-crash-on-invalid-date
Open

Fix submit-event.php crash on non-numeric date fields#1970
svpernova09 wants to merge 1 commit into
php:masterfrom
svpernova09:submit-event-crash-on-invalid-date

Conversation

@svpernova09

Copy link
Copy Markdown
Contributor

Fix submit-event.php crash on non-numeric date fields

Summary

submit-event.php throws an uncaught TypeError whenever a date or recurrence
field is submitted as something other than a number. The raw $_POST values are
passed straight into checkdate() (and mktime()), which under PHP 8 require
int, so any non-numeric string or array value throws.

This was the third-largest source of PHP fatals on www.php.net in a recent 15-day
window of production logs: 9,393 fatal errors, driven by bots and scanners
fuzzing the submission form.

This PR normalizes the numeric event fields to integers at the input boundary,
using a new unit-tested phpweb\Events\EventInput helper.

The bug

On entry the page only maps empty date fields to 0; a non-empty, non-numeric
value passes through unchanged:

$vars = ['sday', 'smonth', 'syear', 'eday', 'emonth', 'eyear', 'recur', 'recur_day'];
foreach ($vars as $varname) {
    if (empty($_POST[$varname])) {
        $_POST[$varname] = 0;
    }
}

The values are then handed directly to checkdate():

if (!checkdate($_POST['smonth'], $_POST['sday'], $_POST['syear'])) {

Under PHP 8, checkdate(int $month, int $day, int $year) rejects a non-numeric
string such as "January" (or an array from ?smonth[]=x) with a TypeError
rather than coercing it. mktime() on the following lines has the same
constraint; it never crashes only because checkdate() throws first.

Evidence from production logs

The fatal (error.log, client IP redacted):

[Sat Jul 04 15:15:46.818798 2026] [php:error] [pid 277780:tid 277780] [client REDACTED] PHP Fatal error:  Uncaught TypeError: checkdate(): Argument #2 ($day) must be of type int, string given in /var/www/sites/www.php.net/submit-event.php:94
Stack trace:
#0 /var/www/sites/www.php.net/submit-event.php(94): checkdate()
#1 {main}
  thrown in /var/www/sites/www.php.net/submit-event.php on line 94

Volume: 9,393 fatals (30 Jun to 14 Jul 2026). They are bursty: 6,856 (73%)
landed on 30 Jun during one campaign.

It hits every date argument, at both checkdate() calls:

Detail Count
Argument #2 ($day) 3,448
Argument #3 ($year) 3,446
Argument #1 ($month) 2,499
given a string 9,286
given an array 107
at submit-event.php:84 (start date) 8,404
at submit-event.php:94 (multi-day end date) 989

Root cause

Untrusted $_POST date fields are passed to type-strict core functions without
being coerced to int. The empty-to-0 normalization only covers missing values,
not garbage ones.

The fix

Coerce the numeric event fields to integers at the input boundary. Anything that
is not a numeric value becomes 0, an invalid date that the existing form
validation already rejects with a friendly "you must specify a valid start date"
message:

$_POST = EventInput::normalizeNumericFields($_POST);

EventInput::normalizeNumericFields() uses is_numeric() before casting, so
non-numeric strings, arrays, and missing values all become 0 (rather than, for
example, (int) "12abc" yielding 12). This protects both the checkdate() and
the mktime() calls, and it replaces the old empty-only loop. Behavior for valid
numeric input is unchanged.

The logic lives in a small src/ class so it can be unit-tested, following the
existing LangChooser pattern; the page script cannot be exercised directly.

Testing

  • New: tests/Unit/Events/EventInputTest.php: 6 tests covering numeric-string
    coercion, non-numeric strings to 0, array values to 0, missing fields to
    0, non-numeric fields left untouched, and a regression test that the
    normalized values are safe to pass to checkdate() without a TypeError.
  • Verified end-to-end against the running site: POSTing non-numeric fields
    (smonth=January&sday=abc), an array field (smonth[]=x), a multi-day event
    with a non-numeric end date, a valid submission, and the empty GET form all
    return HTTP 200 with the form rendered and no fatals in the server log.

Comment thread submit-event.php Outdated
  Review feedback: with EventInput in place, the remaining $_POST
  defaulting block belongs there too, leaving a single write to $_POST.

  Add STRING_FIELDS to EventInput and rename normalizeNumericFields() to
  normalize(), since it now covers both field groups. The string defaults
  use ??=, which matches the previous !isset() check -- an explicit null
  is treated as missing either way.

  Add tests for the string defaults, including that a falsy '0' is
  preserved, and retarget the untouched-fields test at an unmanaged field
  now that type/email are normalized.
@svpernova09
svpernova09 force-pushed the submit-event-crash-on-invalid-date branch from c4f04e5 to 8480fed Compare July 29, 2026 19:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants