CVE-2022-2073: Improper Control of Generation of Code ('Code Injection') (CWE-94)
value: Code Injection in GitHub repository getgrav/grav prior to 1.7.34.
CWE description: The product constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the syntax or behavior of the intended code segment.
CVSS v3 base score: 7.2
Published: 2022-06-29T19:15Z
Repository: https://github.com/getgrav/grav
Language: PHP
Files changed: CHANGELOG.md, system/src/Grav/Common/Twig/Extension/GravExtension.php
Commit date: 2022-06-13 21:11:33 +0300
Commit message: Fixed Twig `|filter()` allowing code execution

Diff: diff --git a/CHANGELOG.md b/CHANGELOG.md
index 19511c0002..0510670b1b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,10 +6,11 @@
     * Ignore upcoming `media.json` file in media
 1. [](#bugfix)
     * Regression: Fixed saving page with a new language causing cache corruption [getgrav/grav-plugin-admin#2282](https://github.com/getgrav/grav-plugin-admin/issues/2282)
     * Fixed a potential fatal error when using watermark in images
     * Fixed `bin/grav install` command with arbitrary destination folder name
+    * Fixed Twig `|filter()` allowing code execution
 
 # v1.7.33
 ## 04/25/2022
 
 1. [](#improved)
diff --git a/system/src/Grav/Common/Twig/Extension/GravExtension.php b/system/src/Grav/Common/Twig/Extension/GravExtension.php
index a2a76b3172..41c8528d83 100644
--- a/system/src/Grav/Common/Twig/Extension/GravExtension.php
+++ b/system/src/Grav/Common/Twig/Extension/GravExtension.php
@@ -7,10 +7,11 @@
  * @license    MIT License; see LICENSE file for details.
  */
 
 namespace Grav\Common\Twig\Extension;
 
+use CallbackFilterIterator;
 use Cron\CronExpression;
 use Grav\Common\Config\Config;
 use Grav\Common\Data\Data;
 use Grav\Common\Debugger;
 use Grav\Common\Grav;
@@ -39,10 +40,11 @@ use Grav\Framework\Psr7\Response;
 use Iterator;
 use JsonSerializable;
 use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
 use Traversable;
 use Twig\Environment;
+use Twig\Error\RuntimeError;
 use Twig\Extension\AbstractExtension;
 use Twig\Extension\GlobalsInterface;
 use Twig\Loader\FilesystemLoader;
 use Twig\TwigFilter;
 use Twig\TwigFunction;
@@ -165,10 +167,13 @@ class GravExtension extends AbstractExtension implements GlobalsInterface
             new TwigFilter('of_type', [$this, 'ofTypeFunc']),
 
             // PHP methods
             new TwigFilter('count', 'count'),
             new TwigFilter('array_diff', 'array_diff'),
+
+            // Security fix
+            new TwigFilter('filter', [$this, 'filterFilter'], ['needs_environment' => true]),
         ];
     }
 
     /**
      * Return a list of all functions.
@@ -1674,6 +1679,22 @@ class GravExtension extends AbstractExtension implements GlobalsInterface
 
             case 'string':
                 return is_string($var);
         }
     }
+
+    /**
+     * @param Environment $env
+     * @param array $array
+     * @param callable|string $arrow
+     * @return array|CallbackFilterIterator
+     * @throws RuntimeError
+     */
+    function filterFilter(Environment $env, $array, $arrow)
+    {
+        if (is_string($arrow) && Utils::isDangerousFunction($arrow)) {
+            throw new RuntimeError('Twig |filter("' . $arrow . '") is not allowed.');
+        }
+
+        return \twig_array_filter($env, $array, $arrow);
+    }
 }

Fixed code:     * Fixed Twig `|filter()` allowing code execution
use CallbackFilterIterator;
use Twig\Error\RuntimeError;

            // Security fix
            new TwigFilter('filter', [$this, 'filterFilter'], ['needs_environment' => true]),

    /**
     * @param Environment $env
     * @param array $array
     * @param callable|string $arrow
     * @return array|CallbackFilterIterator
     * @throws RuntimeError
     */
    function filterFilter(Environment $env, $array, $arrow)
    {
        if (is_string($arrow) && Utils::isDangerousFunction($arrow)) {
            throw new RuntimeError('Twig |filter("' . $arrow . '") is not allowed.');
        }

        return \twig_array_filter($env, $array, $arrow);
    }