Skip to content

Commit 2cad921

Browse files
committed
fix: prevent callable strings
1 parent 35839e9 commit 2cad921

File tree

5 files changed

+76
-13
lines changed

5 files changed

+76
-13
lines changed

composer.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@
1616
"Tests\\E2E\\": "tests/e2e"
1717
}
1818
},
19+
"autoload-dev": {
20+
"psr-4": {
21+
"Utopia\\Http\\Tests\\": "tests/"
22+
}
23+
},
1924
"scripts": {
2025
"lint": "vendor/bin/pint --test",
2126
"format": "vendor/bin/pint",

phpunit.xml

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
1-
<phpunit backupGlobals="false"
2-
backupStaticAttributes="false"
3-
bootstrap="vendor/autoload.php"
4-
colors="true"
5-
convertErrorsToExceptions="true"
6-
convertNoticesToExceptions="true"
7-
convertWarningsToExceptions="true"
8-
processIsolation="false"
9-
stopOnFailure="false"
10-
>
1+
<phpunit
2+
backupGlobals="false"
3+
backupStaticAttributes="false"
4+
bootstrap="vendor/autoload.php"
5+
colors="true"
6+
convertErrorsToExceptions="true"
7+
convertNoticesToExceptions="true"
8+
convertWarningsToExceptions="true"
9+
processIsolation="false"
10+
stopOnFailure="false"
11+
>
1112
<testsuites>
1213
<testsuite name="Application Test Suite">
1314
<file>./tests/e2e/Client.php</file>
1415
<directory>./tests/</directory>
1516
</testsuite>
1617
</testsuites>
17-
</phpunit>
18+
</phpunit>

src/Http/Http.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -740,7 +740,7 @@ protected function getArguments(Hook $hook, string $context, array $values, arra
740740
$paramExists = $existsInRequest || $existsInValues;
741741

742742
$arg = $existsInRequest ? $requestParams[$key] : $param['default'];
743-
if (\is_callable($arg)) {
743+
if (\is_callable($arg) && !\is_string($arg)) {
744744
$arg = \call_user_func_array($arg, $this->getResources($param['injections']));
745745
}
746746
$value = $existsInValues ? $values[$key] : $arg;

tests/HttpTest.php

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ public function providerRouteMatching(): array
454454
/**
455455
* @dataProvider providerRouteMatching
456456
*/
457-
public function testCanMatchRoute(string $method, string $path, string $url = null): void
457+
public function testCanMatchRoute(string $method, string $path, ?string $url = null): void
458458
{
459459
$url ??= $path;
460460
$expected = null;
@@ -616,4 +616,61 @@ public function testWildcardRoute(): void
616616
$_SERVER['REQUEST_METHOD'] = $method;
617617
$_SERVER['REQUEST_URI'] = $uri;
618618
}
619+
620+
public function testCallableStringParametersNotExecuted(): void
621+
{
622+
// Test that callable strings (like function names) are not executed
623+
$route = new Route('GET', '/test-callable-string');
624+
625+
$route
626+
->param('callback', 'phpinfo', new Text(200), 'callback param', true)
627+
->action(function ($callback) {
628+
// If the string 'phpinfo' was executed as a function,
629+
// it would output PHP info. Instead, it should just be the string.
630+
echo 'callback-value: ' . $callback;
631+
});
632+
633+
\ob_start();
634+
$this->http->execute($route, new Request(), '1');
635+
$result = \ob_get_contents();
636+
\ob_end_clean();
637+
638+
$this->assertEquals('callback-value: phpinfo', $result);
639+
640+
// Test with request parameter that is a callable string
641+
$route2 = new Route('GET', '/test-callable-string-param');
642+
643+
$route2
644+
->param('func', 'default', new Text(200), 'func param', false)
645+
->action(function ($func) {
646+
echo 'func-value: ' . $func;
647+
});
648+
649+
\ob_start();
650+
$request = new UtopiaFPMRequestTest();
651+
$request::_setParams(['func' => 'system']);
652+
$this->http->execute($route2, $request, '1');
653+
$result = \ob_get_contents();
654+
\ob_end_clean();
655+
656+
$this->assertEquals('func-value: system', $result);
657+
658+
// Test callable closure still works
659+
$route3 = new Route('GET', '/test-callable-closure');
660+
661+
$route3
662+
->param('generated', function () {
663+
return 'generated-value';
664+
}, new Text(200), 'generated param', true)
665+
->action(function ($generated) {
666+
echo 'generated: ' . $generated;
667+
});
668+
669+
\ob_start();
670+
$this->http->execute($route3, new Request(), '1');
671+
$result = \ob_get_contents();
672+
\ob_end_clean();
673+
674+
$this->assertEquals('generated: generated-value', $result);
675+
}
619676
}

0 commit comments

Comments
 (0)