Skip to content

Commit 6bbeb99

Browse files
authored
Merge pull request #13 from kettasoft/2.2.1
Fix: enforce sanitizing across all filter engines
2 parents 6929dc3 + d284814 commit 6bbeb99

File tree

9 files changed

+85
-34
lines changed

9 files changed

+85
-34
lines changed

src/Contracts/FilterableContext.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,13 @@
88
ExpressionEngineContext,
99
InvokableEngineContext
1010
};
11+
use Kettasoft\Filterable\Sanitization\Sanitizer;
1112

12-
interface FilterableContext extends TreeFilterableContext, RulesetFilterableContect, ExpressionEngineContext, InvokableEngineContext {}
13+
interface FilterableContext extends TreeFilterableContext, RulesetFilterableContect, ExpressionEngineContext, InvokableEngineContext
14+
{
15+
/**
16+
* Get sanitizer instance.
17+
* @return Sanitizer
18+
*/
19+
public function getSanitizerInstance(): Sanitizer;
20+
}

src/Engines/Expression.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public function execute(Builder $builder)
4242
$dissector = Dissector::parse($condition, $this->defaultOperator());
4343

4444
$clause = (new ClauseFactory($this))->make(
45-
new Payload($field, $dissector->operator, $dissector->value, null)
45+
new Payload($field, $dissector->operator, $this->sanitizeValue($field, $dissector->value), $dissector->value)
4646
);
4747

4848
if (! $clause->validated) {

src/Engines/Foundation/Engine.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,17 @@ public function getResources(): Resources
9797
{
9898
return $this->context->getResources();
9999
}
100+
101+
/**
102+
* Sanitize the given value using the sanitizer instance.
103+
*
104+
* @param mixed $filed
105+
* @param mixed $value
106+
*/
107+
final protected function sanitizeValue($filed, $value)
108+
{
109+
$sanitizer = $this->context->getSanitizerInstance();
110+
111+
return $sanitizer->handle($filed, $value);
112+
}
100113
}

src/Engines/Invokeable.php

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -64,29 +64,12 @@ protected function initializeFilters(string $key, string $method, mixed $value):
6464

6565
if (method_exists($this->context, $method)) {
6666

67-
$payload = new Payload($key, $operator, $this->resolveValueSanitizer($key, $val), $val);
67+
$payload = new Payload($key, $operator, $this->sanitizeValue($key, $val), $val);
6868

6969
$this->forwardCallTo($this->context, $method, [$payload]);
7070
}
7171
}
7272

73-
/**
74-
* Run the filter value sanitizer if exist.
75-
* @param string $key
76-
* @param string $method
77-
* @param mixed $value
78-
*/
79-
protected function resolveValueSanitizer(string $key, mixed $value)
80-
{
81-
$sanitizer = $this->context->getSanitizerInstance();
82-
83-
if (!empty($sanitizer->getSanitizers())) {
84-
$value = $sanitizer->handle($key, $value);
85-
}
86-
87-
return $value;
88-
}
89-
9073
/**
9174
* Get method name.
9275
* @param string $filter

src/Engines/Ruleset.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public function execute(Builder $builder): Builder
3737
$dissector = Dissector::parse($dissector, $this->defaultOperator());
3838

3939
$clause = (new ClauseFactory($this))->make(
40-
new Payload($field, $dissector->operator, $dissector->value, null)
40+
new Payload($field, $dissector->operator, $this->sanitizeValue($field, $dissector->value), $dissector->value)
4141
);
4242

4343
if (! $clause->validated) continue;

src/Engines/Tree.php

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@
1010
use Kettasoft\Filterable\Engines\Foundation\Engine;
1111
use Kettasoft\Filterable\Support\RelationPathParser;
1212
use Kettasoft\Filterable\Support\AllowedFieldChecker;
13+
use Kettasoft\Filterable\Engines\Foundation\ClauseApplier;
1314
use Kettasoft\Filterable\Engines\Foundation\ClauseFactory;
1415
use Kettasoft\Filterable\Support\TreeBasedRelationsResolver;
16+
use Kettasoft\Filterable\Engines\Foundation\Appliers\Applier;
1517
use Kettasoft\Filterable\Engines\Contracts\TreeFilterableContext;
1618
use Kettasoft\Filterable\Engines\Contracts\HasAllowedFieldChecker;
1719
use Kettasoft\Filterable\Support\TreeBasedSignelConditionResolver;
@@ -54,27 +56,17 @@ private function applyNode(Builder $builder, TreeNode $node)
5456
} else {
5557

5658
$clause = (new ClauseFactory($this))->make(
57-
new Payload($node->field, $node->operator ?? $this->defaultOperator(), $node->value, null)
59+
new Payload($node->field, $node->operator ?? $this->defaultOperator(), $this->sanitizeValue($node->field, $node->value), $node->value)
5860
);
5961

6062
if (! $clause->validated) {
6163
return $builder; // skip disallowed field
6264
}
6365

64-
[$_, $field] = RelationPathParser::resolve($node->field);
65-
66-
$field = $clause->field;
67-
$operator = $clause->operator;
68-
$value = $clause->value;
69-
7066
if ($clause->isRelational()) {
7167
$clause->relation($this->getResources()->relations)->resolve($builder, $clause);
7268
} else {
73-
if (! AllowedFieldChecker::check($this, $field)) {
74-
return;
75-
}
76-
77-
TreeBasedSignelConditionResolver::resolve($builder, $field, $operator, $value);
69+
Applier::apply(new ClauseApplier($clause), $builder);
7870
}
7971
}
8072

tests/Unit/Engines/ExpressionEngineTest.php

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,24 @@ public function it_can_sent_json_data_to_filtering_operate()
204204
->strict()
205205
->apply(Post::query());
206206

207-
// dd($filter->toRawSql());
207+
$this->assertEquals(15, $filter->count());
208+
}
209+
210+
public function test_it_sanitize_value_before_applying_to_query()
211+
{
212+
$request = Request::create('/posts');
213+
214+
$request->setJson(new InputBag([
215+
'status' => 'PENDING'
216+
]));
217+
218+
$filter = Filterable::withRequest($request)
219+
->setAllowedFields(['status'])
220+
->useEngin(Expression::class)
221+
->setSanitizers([
222+
'status' => fn($value) => strtolower($value)
223+
])
224+
->apply(Post::query());
208225

209226
$this->assertEquals(15, $filter->count());
210227
}

tests/Unit/Engines/RulesetEngineTest.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,4 +186,19 @@ public function it_can_sent_json_data_to_filtering_operate()
186186

187187
$this->assertEquals(15, $filter->count());
188188
}
189+
190+
public function test_it_sanitize_value_before_applying_to_query()
191+
{
192+
$request = Request::create('/posts?status=eq:PENDING');
193+
194+
$filter = Filterable::withRequest($request)
195+
->setAllowedFields(['status'])
196+
->useEngin(Ruleset::class)
197+
->setSanitizers([
198+
'status' => fn($value) => strtolower($value)
199+
])
200+
->apply(Post::query());
201+
202+
$this->assertEquals(15, $filter->count());
203+
}
189204
}

tests/Unit/Engines/TreeEngineTest.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,4 +336,27 @@ public function it_can_filter_with_or_and_logical_operator()
336336

337337
$this->assertEquals(45, $filter->count());
338338
}
339+
340+
public function test_it_sanitize_value_before_applying_to_query()
341+
{
342+
$data = [
343+
"filter" => [
344+
"and" => [
345+
["field" => "status", "operator" => "eq", "value" => "STOPPED"],
346+
['or' => []]
347+
]
348+
]
349+
];
350+
351+
$filter = Filterable::create()
352+
->setData($data, true)
353+
->setAllowedFields(['status'])
354+
->useEngin(Tree::class)
355+
->setSanitizers([
356+
'status' => fn($value) => strtolower($value)
357+
])
358+
->apply(Post::query());
359+
360+
$this->assertEquals(15, $filter->count());
361+
}
339362
}

0 commit comments

Comments
 (0)