Disallow non closures in sort filter when the sanbox mode is enabled

This commit is contained in:
Fabien Potencier
2022-02-04 07:07:46 +01:00
parent e056e6385f
commit 2eb3308055
3 changed files with 18 additions and 15 deletions
+3 -3
View File
@@ -1,10 +1,10 @@
# 2.14.11 (2022-XX-XX) # 2.14.11 (2022-02-04)
* n/a * Fix a security issue when in a sandbox: the `sort` filter must require a Closure for the `arrow` parameter
# 2.14.10 (2022-01-03) # 2.14.10 (2022-01-03)
* Allow more null arguments when Twig expects a string (for better 8.1 support) * Allow more null arguments when Twig expects a string (for better 8.1 support)
# 2.14.9 (2022-01-03) # 2.14.9 (2022-01-03)
+14 -11
View File
@@ -237,7 +237,7 @@ final class CoreExtension extends AbstractExtension
// array helpers // array helpers
new TwigFilter('join', 'twig_join_filter'), new TwigFilter('join', 'twig_join_filter'),
new TwigFilter('split', 'twig_split_filter', ['needs_environment' => true]), new TwigFilter('split', 'twig_split_filter', ['needs_environment' => true]),
new TwigFilter('sort', 'twig_sort_filter'), new TwigFilter('sort', 'twig_sort_filter', ['needs_environment' => true]),
new TwigFilter('merge', 'twig_array_merge'), new TwigFilter('merge', 'twig_array_merge'),
new TwigFilter('batch', 'twig_array_batch'), new TwigFilter('batch', 'twig_array_batch'),
new TwigFilter('column', 'twig_array_column'), new TwigFilter('column', 'twig_array_column'),
@@ -926,7 +926,7 @@ function twig_reverse_filter(Environment $env, $item, $preserveKeys = false)
* *
* @return array * @return array
*/ */
function twig_sort_filter($array, $arrow = null) function twig_sort_filter(Environment $env, $array, $arrow = null)
{ {
if ($array instanceof \Traversable) { if ($array instanceof \Traversable) {
$array = iterator_to_array($array); $array = iterator_to_array($array);
@@ -935,6 +935,8 @@ function twig_sort_filter($array, $arrow = null)
} }
if (null !== $arrow) { if (null !== $arrow) {
twig_check_arrow_in_sandbox($env, $arrow, 'sort', 'filter');
uasort($array, $arrow); uasort($array, $arrow);
} else { } else {
asort($array); asort($array);
@@ -1606,9 +1608,7 @@ function twig_array_filter(Environment $env, $array, $arrow)
throw new RuntimeError(sprintf('The "filter" filter expects an array or "Traversable", got "%s".', \is_object($array) ? \get_class($array) : \gettype($array))); throw new RuntimeError(sprintf('The "filter" filter expects an array or "Traversable", got "%s".', \is_object($array) ? \get_class($array) : \gettype($array)));
} }
if (!$arrow instanceof Closure && $env->hasExtension('\Twig\Extension\SandboxExtension') && $env->getExtension('\Twig\Extension\SandboxExtension')->isSandboxed()) { twig_check_arrow_in_sandbox($env, $arrow, 'filter', 'filter');
throw new RuntimeError('The callable passed to "filter" filter must be a Closure in sandbox mode.');
}
if (\is_array($array)) { if (\is_array($array)) {
return array_filter($array, $arrow, \ARRAY_FILTER_USE_BOTH); return array_filter($array, $arrow, \ARRAY_FILTER_USE_BOTH);
@@ -1620,9 +1620,7 @@ function twig_array_filter(Environment $env, $array, $arrow)
function twig_array_map(Environment $env, $array, $arrow) function twig_array_map(Environment $env, $array, $arrow)
{ {
if (!$arrow instanceof Closure && $env->hasExtension('\Twig\Extension\SandboxExtension') && $env->getExtension('\Twig\Extension\SandboxExtension')->isSandboxed()) { twig_check_arrow_in_sandbox($env, $arrow, 'map', 'filter');
throw new RuntimeError('The callable passed to the "map" filter must be a Closure in sandbox mode.');
}
$r = []; $r = [];
foreach ($array as $k => $v) { foreach ($array as $k => $v) {
@@ -1634,9 +1632,7 @@ function twig_array_map(Environment $env, $array, $arrow)
function twig_array_reduce(Environment $env, $array, $arrow, $initial = null) function twig_array_reduce(Environment $env, $array, $arrow, $initial = null)
{ {
if (!$arrow instanceof Closure && $env->hasExtension('\Twig\Extension\SandboxExtension') && $env->getExtension('\Twig\Extension\SandboxExtension')->isSandboxed()) { twig_check_arrow_in_sandbox($env, $arrow, 'reduce', 'filter');
throw new RuntimeError('The callable passed to the "reduce" filter must be a Closure in sandbox mode.');
}
if (!\is_array($array)) { if (!\is_array($array)) {
if (!$array instanceof \Traversable) { if (!$array instanceof \Traversable) {
@@ -1648,4 +1644,11 @@ function twig_array_reduce(Environment $env, $array, $arrow, $initial = null)
return array_reduce($array, $arrow, $initial); return array_reduce($array, $arrow, $initial);
} }
function twig_check_arrow_in_sandbox(Environment $env, $arrow, $thing, $type)
{
if (!$arrow instanceof Closure && $env->hasExtension('\Twig\Extension\SandboxExtension') && $env->getExtension('\Twig\Extension\SandboxExtension')->isSandboxed()) {
throw new RuntimeError(sprintf('The callable passed to the "%s" %s must be a Closure in sandbox mode.', $thing, $type));
}
}
} }
+1 -1
View File
@@ -390,7 +390,7 @@ EOF
public function testSandboxWithNoClosureFilter() public function testSandboxWithNoClosureFilter()
{ {
$this->expectException('\Twig\Error\RuntimeError'); $this->expectException('\Twig\Error\RuntimeError');
$this->expectExceptionMessage('The callable passed to "filter" filter must be a Closure in sandbox mode in "index" at line 1.'); $this->expectExceptionMessage('The callable passed to the "filter" filter must be a Closure in sandbox mode in "index" at line 1.');
$twig = $this->getEnvironment(true, ['autoescape' => 'html'], ['index' => <<<EOF $twig = $this->getEnvironment(true, ['autoescape' => 'html'], ['index' => <<<EOF
{{ ["foo", "bar", ""]|filter("trim")|join(", ") }} {{ ["foo", "bar", ""]|filter("trim")|join(", ") }}