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

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.

  The event submission form passed $_POST date/recurrence fields straight
  to checkdate() and mktime(). Under PHP 8 these require int, so any
  non-numeric string or array value (bots and scanners fuzzing the form)
  threw an uncaught TypeError -- 9,393 fatals in a 15-day window.

  Normalize the numeric fields to integers at the input boundary via a new
  testable phpweb\Events\EventInput::normalizeNumericFields(); non-numeric
  input becomes 0, an invalid date the existing validation already rejects.

  Add unit tests for the normalization and a regression test that the
  normalized values are safe to pass to checkdate().
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.

1 participant