Skip to content

Commit f8f95d7

Browse files
committed
fix: prevent field label leak through rule parameters in validation messages
getErrorMessage() resolved a rule parameter as a key into the field rules to substitute the referenced field's label. A parameter that was not a field reference (e.g. min_length[secret]) could coincidentally match a defined field name and leak its label into the error message. Label substitution now only applies to field-referencing rules (matches, differs, required_with, required_without).
1 parent c21d575 commit f8f95d7

3 files changed

Lines changed: 63 additions & 1 deletion

File tree

‎system/Validation/Validation.php‎

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,20 @@
3030
*/
3131
class Validation implements ValidationInterface
3232
{
33+
/**
34+
* Rules whose parameter is a reference to another field name.
35+
* Only for these rules the parameter is looked up in the field rules
36+
* to resolve the label of the referenced field.
37+
*
38+
* @var list<string>
39+
*/
40+
private const FIELD_REFERENCE_RULES = [
41+
'matches',
42+
'differs',
43+
'required_with',
44+
'required_without',
45+
];
46+
3347
/**
3448
* Files to load with validation functions.
3549
*
@@ -873,7 +887,10 @@ protected function getErrorMessage(
873887

874888
$args = [
875889
'field' => ($label === null || $label === '') ? $field : lang($label),
876-
'param' => isset($this->rules[$param]['label']) ? lang($this->rules[$param]['label']) : $param,
890+
'param' => in_array($rule, self::FIELD_REFERENCE_RULES, true)
891+
&& isset($this->rules[$param]['label'])
892+
? lang($this->rules[$param]['label'])
893+
: $param,
877894
'value' => $value ?? '',
878895
];
879896

‎tests/system/Validation/ValidationTest.php‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1858,6 +1858,50 @@ public function testNestedArrayThrowsException(): void
18581858
], $this->validation->getErrors());
18591859
}
18601860

1861+
public function testParamIsNotResolvedAsFieldLabelForNonFieldReferenceRules(): void
1862+
{
1863+
$rules = [
1864+
'secret' => [
1865+
'label' => 'Internal Secret Field',
1866+
'rules' => 'required',
1867+
],
1868+
'name' => [
1869+
'label' => 'Name',
1870+
'rules' => 'required|min_length[secret]',
1871+
],
1872+
];
1873+
1874+
$this->validation->setRules($rules);
1875+
1876+
$this->assertFalse($this->validation->run(['name' => 'a', 'secret' => 'x']));
1877+
$this->assertSame(
1878+
['name' => 'The Name field must be at least secret characters in length.'],
1879+
$this->validation->getErrors(),
1880+
);
1881+
}
1882+
1883+
public function testParamResolvesFieldLabelForFieldReferenceRules(): void
1884+
{
1885+
$rules = [
1886+
'password' => [
1887+
'label' => 'Password',
1888+
'rules' => 'required',
1889+
],
1890+
'password_confirmation' => [
1891+
'label' => 'Password Confirmation',
1892+
'rules' => 'matches[password]',
1893+
],
1894+
];
1895+
1896+
$this->validation->setRules($rules);
1897+
1898+
$this->assertFalse($this->validation->run(['password' => 'abc', 'password_confirmation' => 'def']));
1899+
$this->assertSame(
1900+
['password_confirmation' => 'The Password Confirmation field does not match the Password field.'],
1901+
$this->validation->getErrors(),
1902+
);
1903+
}
1904+
18611905
public function testRuleWithLeadingAsterisk(): void
18621906
{
18631907
$data = [

‎user_guide_src/source/changelogs/v4.7.5.rst‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ Bugs Fixed
5959
- **Honeypot:** Fixed a bug where bot detection returned an HTTP 500 response instead of 403 (Forbidden).
6060
- **I18n:** Fixed a bug where ``Time::today()``, ``Time::yesterday()``, and ``Time::tomorrow()`` ignored the specified ``$timezone`` and ``setTestNow()`` when calculating the day.
6161
- **Logger:** Fixed a bug where interpolating a log message with array or non-stringable context values could raise PHP warnings or errors.
62+
- **Validation:** Fixed a bug where a rule parameter that was not a field reference (e.g., a numeric limit) could resolve another field's label and leak it into the error message when it matched a defined field name. Label substitution now only applies to field-referencing rules (``matches``, ``differs``, ``required_with``, ``required_without``).
6263

6364
See the repo's
6465
`CHANGELOG.md <https://github.com/codeigniter4/CodeIgniter4/blob/develop/CHANGELOG.md>`_

0 commit comments

Comments
 (0)