Skip to content

Commit 9e5ca74

Browse files
committed
Merge branch '2.x' into 3.x
* 2.x: Disallow non closures in `sort` filter when the sanbox mode is enabled
2 parents 25d410b + 22b9dc3 commit 9e5ca74

File tree

3 files changed

+16
-13
lines changed

3 files changed

+16
-13
lines changed

CHANGELOG

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# 3.3.8 (2022-XX-XX)
22

3-
* n/a
3+
* Fix a security issue when in a sandbox: the `sort` filter must require a Closure for the `arrow` parameter
44

55
# 3.3.7 (2022-01-03)
66

src/Extension/CoreExtension.php

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ public function getFilters(): array
201201
// array helpers
202202
new TwigFilter('join', 'twig_join_filter'),
203203
new TwigFilter('split', 'twig_split_filter', ['needs_environment' => true]),
204-
new TwigFilter('sort', 'twig_sort_filter'),
204+
new TwigFilter('sort', 'twig_sort_filter', ['needs_environment' => true]),
205205
new TwigFilter('merge', 'twig_array_merge'),
206206
new TwigFilter('batch', 'twig_array_batch'),
207207
new TwigFilter('column', 'twig_array_column'),
@@ -887,7 +887,7 @@ function twig_reverse_filter(Environment $env, $item, $preserveKeys = false)
887887
*
888888
* @return array
889889
*/
890-
function twig_sort_filter($array, $arrow = null)
890+
function twig_sort_filter(Environment $env, $array, $arrow = null)
891891
{
892892
if ($array instanceof \Traversable) {
893893
$array = iterator_to_array($array);
@@ -896,6 +896,8 @@ function twig_sort_filter($array, $arrow = null)
896896
}
897897

898898
if (null !== $arrow) {
899+
twig_check_arrow_in_sandbox($env, $arrow, 'sort', 'filter');
900+
899901
uasort($array, $arrow);
900902
} else {
901903
asort($array);
@@ -1639,9 +1641,7 @@ function twig_array_filter(Environment $env, $array, $arrow)
16391641
throw new RuntimeError(sprintf('The "filter" filter expects an array or "Traversable", got "%s".', \is_object($array) ? \get_class($array) : \gettype($array)));
16401642
}
16411643

1642-
if (!$arrow instanceof Closure && $env->hasExtension('\Twig\Extension\SandboxExtension') && $env->getExtension('\Twig\Extension\SandboxExtension')->isSandboxed()) {
1643-
throw new RuntimeError('The callable passed to "filter" filter must be a Closure in sandbox mode.');
1644-
}
1644+
twig_check_arrow_in_sandbox($env, $arrow, 'filter', 'filter');
16451645

16461646
if (\is_array($array)) {
16471647
return array_filter($array, $arrow, \ARRAY_FILTER_USE_BOTH);
@@ -1653,9 +1653,7 @@ function twig_array_filter(Environment $env, $array, $arrow)
16531653

16541654
function twig_array_map(Environment $env, $array, $arrow)
16551655
{
1656-
if (!$arrow instanceof Closure && $env->hasExtension('\Twig\Extension\SandboxExtension') && $env->getExtension('\Twig\Extension\SandboxExtension')->isSandboxed()) {
1657-
throw new RuntimeError('The callable passed to the "map" filter must be a Closure in sandbox mode.');
1658-
}
1656+
twig_check_arrow_in_sandbox($env, $arrow, 'map', 'filter');
16591657

16601658
$r = [];
16611659
foreach ($array as $k => $v) {
@@ -1667,9 +1665,7 @@ function twig_array_map(Environment $env, $array, $arrow)
16671665

16681666
function twig_array_reduce(Environment $env, $array, $arrow, $initial = null)
16691667
{
1670-
if (!$arrow instanceof Closure && $env->hasExtension('\Twig\Extension\SandboxExtension') && $env->getExtension('\Twig\Extension\SandboxExtension')->isSandboxed()) {
1671-
throw new RuntimeError('The callable passed to the "reduce" filter must be a Closure in sandbox mode.');
1672-
}
1668+
twig_check_arrow_in_sandbox($env, $arrow, 'reduce', 'filter');
16731669

16741670
if (!\is_array($array)) {
16751671
if (!$array instanceof \Traversable) {
@@ -1681,4 +1677,11 @@ function twig_array_reduce(Environment $env, $array, $arrow, $initial = null)
16811677

16821678
return array_reduce($array, $arrow, $initial);
16831679
}
1680+
1681+
function twig_check_arrow_in_sandbox(Environment $env, $arrow, $thing, $type)
1682+
{
1683+
if (!$arrow instanceof Closure && $env->hasExtension('\Twig\Extension\SandboxExtension') && $env->getExtension('\Twig\Extension\SandboxExtension')->isSandboxed()) {
1684+
throw new RuntimeError(sprintf('The callable passed to the "%s" %s must be a Closure in sandbox mode.', $thing, $type));
1685+
}
1686+
}
16841687
}

tests/Extension/SandboxTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,7 @@ public function testSandboxDisabledAfterIncludeFunctionError()
390390
public function testSandboxWithNoClosureFilter()
391391
{
392392
$this->expectException('\Twig\Error\RuntimeError');
393-
$this->expectExceptionMessage('The callable passed to "filter" filter must be a Closure in sandbox mode in "index" at line 1.');
393+
$this->expectExceptionMessage('The callable passed to the "filter" filter must be a Closure in sandbox mode in "index" at line 1.');
394394

395395
$twig = $this->getEnvironment(true, ['autoescape' => 'html'], ['index' => <<<EOF
396396
{{ ["foo", "bar", ""]|filter("trim")|join(", ") }}

0 commit comments

Comments
 (0)