Skip to content

Commit 249c915

Browse files
committed
fix: add missing validation for placeholder field
1 parent 601d98e commit 249c915

File tree

3 files changed

+66
-25
lines changed

3 files changed

+66
-25
lines changed

system/Validation/Validation.php

Lines changed: 47 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616
use CodeIgniter\HTTP\RequestInterface;
1717
use CodeIgniter\Validation\Exceptions\ValidationException;
1818
use CodeIgniter\View\RendererInterface;
19+
use Config\Services;
1920
use Config\Validation as ValidationConfig;
2021
use InvalidArgumentException;
22+
use LogicException;
2123
use TypeError;
2224

2325
/**
@@ -660,47 +662,70 @@ public function loadRuleGroup(?string $group = null)
660662
*
661663
* and the following rule:
662664
*
663-
* 'required|is_unique[users,email,id,{id}]'
665+
* 'is_unique[users,email,id,{id}]'
664666
*
665667
* The value of {id} would be replaced with the actual id in the form data:
666668
*
667-
* 'required|is_unique[users,email,id,13]'
669+
* 'is_unique[users,email,id,13]'
668670
*/
669671
protected function fillPlaceholders(array $rules, array $data): array
670672
{
671-
$replacements = [];
673+
foreach ($rules as &$rule) {
674+
$ruleSet = $rule['rules'];
672675

673-
foreach ($data as $key => $value) {
674-
$replacements["{{$key}}"] = $value;
675-
}
676+
foreach ($ruleSet as &$row) {
677+
if (is_string($row)) {
678+
$placeholderFields = $this->retrievePlaceholders($row, $data);
679+
680+
foreach ($placeholderFields as $field) {
681+
$validator ??= Services::validation(null, false);
676682

677-
if ($replacements !== []) {
678-
foreach ($rules as &$rule) {
679-
$ruleSet = $rule['rules'] ?? $rule;
683+
$placeholderRules = $rules[$field]['rules'] ?? null;
680684

681-
if (is_array($ruleSet)) {
682-
foreach ($ruleSet as &$row) {
683-
if (is_string($row)) {
684-
$row = strtr($row, $replacements);
685+
// Check if the validation rule for the placeholder exists
686+
if ($placeholderRules === null) {
687+
throw new LogicException(
688+
'No validation rules for the placeholder: ' . $field
689+
);
685690
}
686-
}
687-
}
688691

689-
if (is_string($ruleSet)) {
690-
$ruleSet = strtr($ruleSet, $replacements);
691-
}
692+
// Check if the rule does not have placeholders
693+
foreach ($placeholderRules as $placeholderRule) {
694+
if ($this->retrievePlaceholders($placeholderRule, $data)) {
695+
throw new LogicException(
696+
'The placeholder field cannot use placeholder: ' . $field
697+
);
698+
}
699+
}
700+
701+
// Validate the placeholder field
702+
if (! $validator->check($data[$field], implode('|', $placeholderRules))) {
703+
// if fails, do nothing
704+
continue;
705+
}
692706

693-
if (isset($rule['rules'])) {
694-
$rule['rules'] = $ruleSet;
695-
} else {
696-
$rule = $ruleSet;
707+
// Replace the placeholder in the rule
708+
$ruleSet = str_replace('{' . $field . '}', $data[$field], $ruleSet);
709+
}
697710
}
698711
}
712+
713+
$rule['rules'] = $ruleSet;
699714
}
700715

701716
return $rules;
702717
}
703718

719+
/**
720+
* Retrieves valid placeholder fields.
721+
*/
722+
private function retrievePlaceholders(string $rule, array $data): array
723+
{
724+
preg_match_all('/{(.+?)}/', $rule, $matches);
725+
726+
return array_intersect($matches[1], array_keys($data));
727+
}
728+
704729
/**
705730
* Checks to see if an error exists for the given field.
706731
*/

tests/system/Validation/StrictRules/DatabaseRelatedRulesTest.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,10 @@ public function testIsUniqueIgnoresParamsPlaceholders(): void
121121
'email' => '[email protected]',
122122
];
123123

124-
$this->validation->setRules(['email' => 'is_unique[user.email,id,{id}]']);
124+
$this->validation->setRules([
125+
'id' => 'is_natural_no_zero',
126+
'email' => 'is_unique[user.email,id,{id}]',
127+
]);
125128
$this->assertTrue($this->validation->run($data));
126129
}
127130

@@ -221,7 +224,10 @@ public function testIsNotUniqueIgnoresParamsPlaceholders(): void
221224
'id' => $row->id,
222225
'email' => '[email protected]',
223226
];
224-
$this->validation->setRules(['email' => 'is_not_unique[user.email,id,{id}]']);
227+
$this->validation->setRules([
228+
'id' => 'is_natural_no_zero',
229+
'email' => 'is_not_unique[user.email,id,{id}]',
230+
]);
225231
$this->assertTrue($this->validation->run($data));
226232
}
227233

tests/system/Validation/ValidationTest.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1391,7 +1391,7 @@ public function provideStringRulesCases(): iterable
13911391
protected function placeholderReplacementResultDetermination(string $placeholder = 'id', ?array $data = null)
13921392
{
13931393
if ($data === null) {
1394-
$data = [$placeholder => 'placeholder-value'];
1394+
$data = [$placeholder => '12'];
13951395
}
13961396

13971397
$validationRules = $this->getPrivateMethodInvoker($this->validation, 'fillPlaceholders')($this->validation->getRules(), $data);
@@ -1426,13 +1426,15 @@ public function testPlaceholderReplacementTestFails()
14261426

14271427
public function testPlaceholderReplacementSetSingleRuleString()
14281428
{
1429+
$this->validation->setRule('id', null, 'required|is_natural_no_zero');
14291430
$this->validation->setRule('foo', null, 'required|filter[{id}]');
14301431

14311432
$this->placeholderReplacementResultDetermination();
14321433
}
14331434

14341435
public function testPlaceholderReplacementSetSingleRuleArray()
14351436
{
1437+
$this->validation->setRule('id', null, ['required', 'is_natural_no_zero']);
14361438
$this->validation->setRule('foo', null, ['required', 'filter[{id}]']);
14371439

14381440
$this->placeholderReplacementResultDetermination();
@@ -1441,6 +1443,7 @@ public function testPlaceholderReplacementSetSingleRuleArray()
14411443
public function testPlaceholderReplacementSetMultipleRulesSimpleString()
14421444
{
14431445
$this->validation->setRules([
1446+
'id' => 'required|is_natural_no_zero',
14441447
'foo' => 'required|filter[{id}]',
14451448
]);
14461449

@@ -1450,6 +1453,7 @@ public function testPlaceholderReplacementSetMultipleRulesSimpleString()
14501453
public function testPlaceholderReplacementSetMultipleRulesSimpleArray()
14511454
{
14521455
$this->validation->setRules([
1456+
'id' => ['required', 'is_natural_no_zero'],
14531457
'foo' => ['required', 'filter[{id}]'],
14541458
]);
14551459

@@ -1459,6 +1463,9 @@ public function testPlaceholderReplacementSetMultipleRulesSimpleArray()
14591463
public function testPlaceholderReplacementSetMultipleRulesComplexString()
14601464
{
14611465
$this->validation->setRules([
1466+
'id' => [
1467+
'rules' => 'required|is_natural_no_zero',
1468+
],
14621469
'foo' => [
14631470
'rules' => 'required|filter[{id}]',
14641471
],
@@ -1470,6 +1477,9 @@ public function testPlaceholderReplacementSetMultipleRulesComplexString()
14701477
public function testPlaceholderReplacementSetMultipleRulesComplexArray()
14711478
{
14721479
$this->validation->setRules([
1480+
'id' => [
1481+
'rules' => ['required', 'is_natural_no_zero'],
1482+
],
14731483
'foo' => [
14741484
'rules' => ['required', 'filter[{id}]'],
14751485
],

0 commit comments

Comments
 (0)