Skip to content

Fix validator regex alternation, array input 500s and duplicated errors - #394

Merged
techmahedy merged 1 commit into
doppar:4.xfrom
techmahedy:fix/validator-pipe-array-errors
Oct 30, 2026
Merged

techmahedy merged 1 commit into
doppar:4.xfrom
techmahedy:fix/validator-pipe-array-errors

Conversation

@techmahedy

Copy link
Copy Markdown
Member

Pull Request Checklist

Q A
Branch? 4.x
Bug fix? yes
New feature? no
Deprecations? no
Issues -
License MIT

Description

Three bugs in the request validator (Sanitizer and the Rule trait behind $request->sanitize()), found by running every rule against awkward input.

1. A | inside a regex rule was treated as a rule separator

Both rule loops did explode('|', $rules), so regex:/^(cat|dog)$/ became two broken rules and could never match. cat, dog and cow all failed.

splitRules() in the ValidationRules trait now keeps a regex: rule together until its closing delimiter. It handles flags (/i), other delimiters (#, ~, {}, ()), an escaped delimiter inside the pattern, and a pattern ending in an escaped backslash. An unterminated pattern keeps its own piece and does not swallow the rules after it. Both Sanitizer::validate() and Rule::sanitize() use it.

2. Array or object input crashed the request

A query string like ?name[]=x, or JSON like {"name":["x"]}, made 25 rules throw a TypeError or Error (or emit an "Array to string conversion" warning) instead of failing validation. Affected rules: min, max, date, gte, lte, gt, lt, alpha, alpha_num, alpha_dash, regex, phone, uuid, starts_with, ends_with, date_format, uppercase, lowercase, slug, digits, min_digits, max_digits, same_as, unique and exists_in. Putting string before min did not help, because later rules still run. unique and exists_in also looked up the literal string "Array" in the database.

sanitizeUserRequest now fails these fields with the "must be a string" message. Objects are rejected for every rule except required, array, string and null. A string containing a NUL byte now fails date_format instead of throwing a ValueError.

On the live app, POST /login with password[]=x returned 500 before and 302 after (the normal redirect-back-with-errors).

3. Errors piled up when validating more than once

validate() never cleared $this->errors, so calling fails() twice, or fails() then validate(), listed the same message repeatedly. It now resets first. Identical messages for one field are also shown only once, which keeps required|string|min:3 on an array to a single error.

Changes

  • ValidationRules: splitRules(), isRegexClosed(), rejectsNonScalarValue(), the guard in sanitizeUserRequest, and the NUL-byte check in isDateFormat.
  • Sanitizer: uses splitRules(), resets errors in validate(), de-duplicates messages in addError().
  • Rule: uses splitRules() and de-duplicates messages.
  • Added ValidationHardeningTest with 104 tests, including the previously untested Rule::sanitize() path.

Checklist

  • Tests have been added or updated
  • Documentation has been updated if necessary
  • Code follows the project coding standards
  • All tests pass locally

@techmahedy techmahedy added the bug Something isn't working label Oct 30, 2026
@techmahedy
techmahedy merged commit 1b2ee85 into doppar:4.x Oct 30, 2026
16 checks passed
@techmahedy
techmahedy deleted the fix/validator-pipe-array-errors branch October 30, 2026 16:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant