fix: Handle null byte when validating date/time formats - #939
Conversation
7096e37 to
e0cda49
Compare
There was a problem hiding this comment.
Pull request overview
Fixes date/time format validation for null-byte inputs by handling parser exceptions across legacy and draft validators.
Changes:
- Handles date/time parsing failures.
- Adds regression cases across supported drafts.
- Updates the PHPStan baseline for the new return type.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Review summary |
|---|---|
tests/Constraints/FormatConstraintTest.php |
NUL-byte test input is incorrectly single-quoted and does not exercise the failure path. |
tests/Constraints/Draft2019/FormatConstraintTest.php |
NUL-byte test inputs are incorrectly single-quoted. |
tests/Constraints/Draft07/FormatConstraintTest.php |
NUL-byte test inputs are incorrectly single-quoted. |
tests/Constraints/Draft06/FormatConstraintTest.php |
NUL-byte test inputs are incorrectly single-quoted. |
src/JsonSchema/Constraints/FormatConstraint.php |
Adding : bool creates a backwards-incompatible protected method signature change. |
src/JsonSchema/Constraints/Drafts/Draft2019/FormatConstraint.php |
Updates Draft 2019 date/time validation. |
src/JsonSchema/Constraints/Drafts/Draft07/FormatConstraint.php |
Updates Draft 07 date/time validation. |
src/JsonSchema/Constraints/Drafts/Draft06/FormatConstraint.php |
Updates Draft 06 date/time validation. |
phpstan-baseline.neon |
Updates the baseline for the return-type change. |
Suppressed comments (5)
src/JsonSchema/Constraints/Drafts/Draft06/FormatConstraint.php:131
- Catching
Throwablehere turns any unrelatedErroror exception fromDateTime::createFromFormat()into an ordinary format failure, which can hide runtime/programming defects. The intended PHP 8 failure is specifically the null-byte case; reject\0(or otherwise rethrow unexpected throwables) and preserve other failures.
} catch (\Throwable $e) {
return false;
src/JsonSchema/Constraints/Drafts/Draft07/FormatConstraint.php:155
- Catching
Throwablehere turns any unrelatedErroror exception fromDateTimeImmutable::createFromFormat()into an ordinary format failure, which can hide runtime/programming defects. The intended PHP 8 failure is specifically the null-byte case; reject\0(or otherwise rethrow unexpected throwables) and preserve other failures.
} catch (\Throwable $e) {
return false;
src/JsonSchema/Constraints/Drafts/Draft2019/FormatConstraint.php:146
- Catching
Throwablehere turns any unrelatedErroror exception fromDateTimeImmutable::createFromFormat()into an ordinary format failure, which can hide runtime/programming defects. The intended PHP 8 failure is specifically the null-byte case; reject\0(or otherwise rethrow unexpected throwables) and preserve other failures.
} catch (\Throwable $e) {
return false;
src/JsonSchema/Constraints/FormatConstraint.php:161
- Catching
Throwablehere turns any unrelatedErroror exception fromDateTime::createFromFormat()into an ordinary format failure, which can hide runtime/programming defects. The intended PHP 8 failure is specifically the null-byte case; since this package supports PHP 7.2, reject\0(or otherwise rethrow unexpected throwables) and preserve other failures.
} catch (Throwable $e) {
return false;
tests/Constraints/FormatConstraintTest.php:199
- This is a single-quoted PHP string, so
\x00remains the literal characters backslash,x,0,0; it does not contain a NUL byte. As a result, this case passes even before the new exception handling and does not reproduce the reported failure. Use a double-quoted string (or concatenate"\x00") here.
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
9c3a15a to
3e85f69
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.
Suppressed comments (13)
tests/Constraints/Draft06/FormatConstraintTest.php:5
- Please keep this test in the project's test namespace. Existing tests use
JsonSchema\Tests\Constraints...(for example,tests/Constraints/DefaultPropertiesTest.php:5), andautoload-devonly maps theJsonSchema\Tests\prefix;Constraints\Draft06leaves this class outside the configured test namespace.
namespace Constraints\Draft06;
tests/Constraints/Draft07/FormatConstraintTest.php:28
- This provider key says
date-time, but the format argument is'date'. Thedate-timepath usesvalidateRfc3339DateTime()rather than the changed date/time helper, so this case does not test the format it advertises and can give a false impression of coverage. Rename the case to describe the date format (or change the argument if a separate date-time test is intended).
yield 'Date-time format with value containing null byte' => ["2020-01-01T12:34:56\x00", 'date'];
tests/Constraints/Draft07/FormatConstraintTest.php:5
- Please keep this test in the project's test namespace. Existing tests use
JsonSchema\Tests\Constraints...(for example,tests/Constraints/DefaultPropertiesTest.php:5), andautoload-devonly maps theJsonSchema\Tests\prefix;Constraints\Draft07leaves this class outside the configured test namespace.
namespace Constraints\Draft07;
tests/Constraints/Draft2019/FormatConstraintTest.php:28
- This provider key says
date-time, but the format argument is'date'. Thedate-timepath usesvalidateRfc3339DateTime()rather than the changed date/time helper, so this case does not test the format it advertises and can give a false impression of coverage. Rename the case to describe the date format (or change the argument if a separate date-time test is intended).
yield 'Date-time format with value containing null byte' => ["2020-01-01T12:34:56\x00", 'date'];
tests/Constraints/Draft2019/FormatConstraintTest.php:5
- Please keep this test in the project's test namespace. Existing tests use
JsonSchema\Tests\Constraints...(for example,tests/Constraints/DefaultPropertiesTest.php:5), andautoload-devonly maps theJsonSchema\Tests\prefix;Constraints\Draft2019leaves this class outside the configured test namespace.
namespace Constraints\Draft2019;
tests/Constraints/FormatConstraintTest.php:204
utc-millisecalso routes through this date-time helper (theUformat), but the new regression cases cover onlydateandtime. Add a NUL-byte case forutc-millisecso this affected format is protected as well.
src/JsonSchema/Constraints/Drafts/Draft06/FormatConstraint.php:132- Please avoid catching
Throwablehere. This helper only needs to turn the NUL-relatedValueErrorinto a validation failure; catching everyThrowablealso hides unrelatedTypeError/Errorfailures fromDateTime::createFromFormatand makes them look like invalid input. Astrpos($datetime, "\\0")guard before the call preserves the other failures.
try {
$dt = \DateTime::createFromFormat($format, $datetime);
} catch (\Throwable $e) {
return false;
}
src/JsonSchema/Constraints/Drafts/Draft07/FormatConstraint.php:156
- Please avoid catching
Throwablehere. This helper only needs to turn the NUL-relatedValueErrorinto a validation failure; catching everyThrowablealso hides unrelatedTypeError/Errorfailures fromDateTimeImmutable::createFromFormatand makes them look like invalid input. Astrpos($datetime, "\\0")guard before the call preserves the other failures.
try {
$dt = \DateTimeImmutable::createFromFormat($format, $input);
} catch (\Throwable $e) {
return false;
}
src/JsonSchema/Constraints/Drafts/Draft2019/FormatConstraint.php:147
- Please avoid catching
Throwablehere. This helper only needs to turn the NUL-relatedValueErrorinto a validation failure; catching everyThrowablealso hides unrelatedTypeError/Errorfailures fromDateTimeImmutable::createFromFormatand makes them look like invalid input. Astrpos($datetime, "\\0")guard before the call preserves the other failures.
try {
$dt = \DateTimeImmutable::createFromFormat($format, $input);
} catch (\Throwable $e) {
return false;
}
src/JsonSchema/Constraints/FormatConstraint.php:162
- Please avoid catching
Throwablehere. The reported failure is theValueErrorfor a NUL argument, but this also swallowsTypeErrorand otherErrorinstances fromDateTime::createFromFormat, silently converting internal/runtime failures into ordinary format violations. Reject the NUL before the call (usingstrposfor this package's PHP 7.2 support) or catch only the expected exception.
try {
$dt = \DateTime::createFromFormat($format, (string) $datetime);
} catch (Throwable $e) {
return false;
}
tests/Constraints/Draft06/FormatConstraintTest.php:28
- This provider key says
date-time, but the format argument is'date'. Thedate-timepath usesvalidateRfc3339DateTime()rather than the changed date/time helper, so this case does not test the format it advertises and can give a false impression of coverage. Rename the case to describe the date format (or change the argument if a separate date-time test is intended).
yield 'Date-time format with value containing null byte' => ["2020-01-01T12:34:56\x00", 'date'];
tests/Constraints/Draft07/FormatConstraintTest.php:32
utc-millisecalso routes through this date-time helper (theUformat), but the new regression cases cover onlydateandtime. Add a NUL-byte case forutc-millisecso this affected format is protected as well.
yield 'Time format with value containing null byte' => ["13:37:00\x00", 'time'];
tests/Constraints/Draft2019/FormatConstraintTest.php:32
utc-millisecalso routes through this date-time helper (theUformat), but the new regression cases cover onlydateandtime. Add a NUL-byte case forutc-millisecso this affected format is protected as well.
yield 'Time format with value containing null byte' => ["13:37:00\x00", 'time'];
3e85f69 to
bf139b0
Compare
Description
This PR will add handling of the
ValueErrorthrown when creatingDateTimewith a null byte (\x00)Related Issue
Closes #937
Type of Change
Checklist
Additional Notes