CVE-2020-28925: Insufficient Information (NVD-CWE-noinfo)
value: Bolt before 3.7.2 does not restrict filter options in a Request in the Twig context, and is therefore inconsistent with the "How to Harden Your PHP for Better Security" guidance.
CWE description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
CVSS v3 base score: 5.3
Published: 2020-12-30T19:15Z
Repository: https://github.com/bolt/bolt
Language: PHP
Files changed: src/Twig/ArrayAccessSecurityProxy.php, src/Twig/Request/FileBag.php, src/Twig/Request/ParameterBag.php, src/Twig/Request/RestrictedFilterTrait.php, src/Twig/Request/ServerBag.php
Commit date: 2020-10-16 15:10:28 +0200
Commit message: Restrict `filter` options in `Request` in Twig context

Diff: diff --git a/src/Twig/ArrayAccessSecurityProxy.php b/src/Twig/ArrayAccessSecurityProxy.php
index f454ba76c7..b814dcdd16 100644
--- a/src/Twig/ArrayAccessSecurityProxy.php
+++ b/src/Twig/ArrayAccessSecurityProxy.php
@@ -70,10 +70,23 @@ class ArrayAccessSecurityProxy implements ArrayAccess, Countable, IteratorAggreg
      */
     public function offsetGet($offset)
     {
         $this->sandbox->checkPropertyAllowed($this, $offset);
 
+        if ($offset === 'request') {
+            $request = $this->object[$offset];
+
+            $request->request = new Request\ParameterBag($request->request->all());
+            $request->query = new Request\ParameterBag($request->query->all());
+            $request->attributes = new Request\ParameterBag($request->attributes->all());
+            $request->cookies = new Request\ParameterBag($request->cookies->all());
+            $request->files = new Request\FileBag($request->files->all());
+            $request->server = new Request\ServerBag($request->server->all());
+
+            return $request;
+        }
+
         return $this->object[$offset];
     }
 
     /**
      * {@inheritdoc}
diff --git a/src/Twig/Request/FileBag.php b/src/Twig/Request/FileBag.php
new file mode 100644
index 0000000000..6b6bab35ab
--- /dev/null
+++ b/src/Twig/Request/FileBag.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace Bolt\Twig\Request;
+
+/**
+ * ParameterBag is a container for key/value pairs.
+ * Overridden in order to disable certain filters.
+ *
+ * @author Xiao-Hu Tai <xiao@twokings.nl>
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class FileBag extends \Symfony\Component\HttpFoundation\FileBag implements \IteratorAggregate, \Countable
+{
+    use RestrictedFilterTrait;
+}
diff --git a/src/Twig/Request/ParameterBag.php b/src/Twig/Request/ParameterBag.php
new file mode 100644
index 0000000000..9d0d77ceb1
--- /dev/null
+++ b/src/Twig/Request/ParameterBag.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace Bolt\Twig\Request;
+
+/**
+ * ParameterBag is a container for key/value pairs.
+ * Overridden in order to disable certain filters.
+ *
+ * @author Xiao-Hu Tai <xiao@twokings.nl>
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class ParameterBag extends \Symfony\Component\HttpFoundation\ParameterBag implements \IteratorAggregate, \Countable
+{
+    use RestrictedFilterTrait;
+}
diff --git a/src/Twig/Request/RestrictedFilterTrait.php b/src/Twig/Request/RestrictedFilterTrait.php
new file mode 100644
index 0000000000..f6a15d4f38
--- /dev/null
+++ b/src/Twig/Request/RestrictedFilterTrait.php
@@ -0,0 +1,102 @@
+<?php
+
+namespace Bolt\Twig\Request;
+
+/**
+ * Override filter function in order to restrict certain options in Twig context.
+ * The list of restricted options are taken from "How to Harden Your PHP for
+ * Better Security" [1].
+ *
+ * [1] https://howtogetonline.com/how-to-harden-your-php-for-better-security.php
+ *
+ * @author Xiao-Hu Tai <xiao@twokings.nl>
+ */
+trait RestrictedFilterTrait
+{
+    /** @var array */
+    private $restrictedOptions = [
+        '_getppid',
+        'allow_url_fopen',
+        'allow_url_include',
+        'chgrp',
+        'chmod',
+        'chown',
+        'curl_exec',
+        'curl_multi_exec',
+        'diskfreespace',
+        'dl',
+        'exec',
+        'fpaththru',
+        'getmypid',
+        'getmyuid',
+        'highlight_file',
+        'ignore_user_abord',
+        'ini_set',
+        'lchgrp',
+        'lchown',
+        'leak',
+        'link',
+        'listen',
+        'parse_ini_file',
+        'passthru',
+        'pcntl_exec',
+        'php_uname',
+        'phpinfo',
+        'popen',
+        'posix_ctermid',
+        'posix_getcwd',
+        'posix_getegid',
+        'posix_geteuid',
+        'posix_getgid',
+        'posix_getgrgid',
+        'posix_getgrnam',
+        'posix_getgroups',
+        'posix_getlogin',
+        'posix_getpgid',
+        'posix_getpgrp',
+        'posix_getpid',
+        'posix_getpwnam',
+        'posix_getpwuid',
+        'posix_getrlimit',
+        'posix_getsid',
+        'posix_getuid',
+        'posix_isatty',
+        'posix_kill',
+        'posix_mkfifo',
+        'posix_setegid',
+        'posix_seteuid',
+        'posix_setgid',
+        'posix_setpgid',
+        'posix_setsid',
+        'posix_setuid',
+        'posix_times',
+        'posix_ttyname',
+        'posix_uname',
+        'posix',
+        'proc_close',
+        'proc_get_status',
+        'proc_nice',
+        'proc_open',
+        'proc_terminate',
+        'putenv',
+        'set_time_limit',
+        'shell_exec',
+        'show_source',
+        'source',
+        'system',
+        'tmpfile',
+        'virtual',
+    ];
+
+    /**
+     * {@inheritdoc}
+     */
+    public function filter($key, $default = null, $filter = FILTER_DEFAULT, $options = array(), $deep = false)
+    {
+        if (isset($options['options']) && in_array($options['options'], $this->restrictedOptions)) {
+            unset($options['options']);
+        }
+
+        return parent::filter($key, $default, $filter, $options, $deep);
+    }
+}
diff --git a/src/Twig/Request/ServerBag.php b/src/Twig/Request/ServerBag.php
new file mode 100644
index 0000000000..666f380cb0
--- /dev/null
+++ b/src/Twig/Request/ServerBag.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace Bolt\Twig\Request;
+
+/**
+ * ParameterBag is a container for key/value pairs.
+ * Overridden in order to disable certain filters.
+ *
+ * @author Xiao-Hu Tai <xiao@twokings.nl>
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class ServerBag extends \Symfony\Component\HttpFoundation\ServerBag implements \IteratorAggregate, \Countable
+{
+    use RestrictedFilterTrait;
+}

Fixed code:         if ($offset === 'request') {
            $request = $this->object[$offset];

            $request->request = new Request\ParameterBag($request->request->all());
            $request->query = new Request\ParameterBag($request->query->all());
            $request->attributes = new Request\ParameterBag($request->attributes->all());
            $request->cookies = new Request\ParameterBag($request->cookies->all());
            $request->files = new Request\FileBag($request->files->all());
            $request->server = new Request\ServerBag($request->server->all());

            return $request;
        }

<?php

namespace Bolt\Twig\Request;

/**
 * ParameterBag is a container for key/value pairs.
 * Overridden in order to disable certain filters.
 *
 * @author Xiao-Hu Tai <xiao@twokings.nl>
 * @author Fabien Potencier <fabien@symfony.com>
 */
class FileBag extends \Symfony\Component\HttpFoundation\FileBag implements \IteratorAggregate, \Countable
{
    use RestrictedFilterTrait;
}
<?php

namespace Bolt\Twig\Request;

/**
 * ParameterBag is a container for key/value pairs.
 * Overridden in order to disable certain filters.
 *
 * @author Xiao-Hu Tai <xiao@twokings.nl>
 * @author Fabien Potencier <fabien@symfony.com>
 */
class ParameterBag extends \Symfony\Component\HttpFoundation\ParameterBag implements \IteratorAggregate, \Countable
{
    use RestrictedFilterTrait;
}
<?php

namespace Bolt\Twig\Request;

/**
 * Override filter function in order to restrict certain options in Twig context.
 * The list of restricted options are taken from "How to Harden Your PHP for
 * Better Security" [1].
 *
 * [1] https://howtogetonline.com/how-to-harden-your-php-for-better-security.php
 *
 * @author Xiao-Hu Tai <xiao@twokings.nl>
 */
trait RestrictedFilterTrait
{
    /** @var array */
    private $restrictedOptions = [
        '_getppid',
        'allow_url_fopen',
        'allow_url_include',
        'chgrp',
        'chmod',
        'chown',
        'curl_exec',
        'curl_multi_exec',
        'diskfreespace',
        'dl',
        'exec',
        'fpaththru',
        'getmypid',
        'getmyuid',
        'highlight_file',
        'ignore_user_abord',
        'ini_set',
        'lchgrp',
        'lchown',
        'leak',
        'link',
        'listen',
        'parse_ini_file',
        'passthru',
        'pcntl_exec',
        'php_uname',
        'phpinfo',
        'popen',
        'posix_ctermid',
        'posix_getcwd',
        'posix_getegid',
        'posix_geteuid',
        'posix_getgid',
        'posix_getgrgid',
        'posix_getgrnam',
        'posix_getgroups',
        'posix_getlogin',
        'posix_getpgid',
        'posix_getpgrp',
        'posix_getpid',
        'posix_getpwnam',
        'posix_getpwuid',
        'posix_getrlimit',
        'posix_getsid',
        'posix_getuid',
        'posix_isatty',
        'posix_kill',
        'posix_mkfifo',
        'posix_setegid',
        'posix_seteuid',
        'posix_setgid',
        'posix_setpgid',
        'posix_setsid',
        'posix_setuid',
        'posix_times',
        'posix_ttyname',
        'posix_uname',
        'posix',
        'proc_close',
        'proc_get_status',
        'proc_nice',
        'proc_open',
        'proc_terminate',
        'putenv',
        'set_time_limit',
        'shell_exec',
        'show_source',
        'source',
        'system',
        'tmpfile',
        'virtual',
    ];

    /**
     * {@inheritdoc}
     */
    public function filter($key, $default = null, $filter = FILTER_DEFAULT, $options = array(), $deep = false)
    {
        if (isset($options['options']) && in_array($options['options'], $this->restrictedOptions)) {
            unset($options['options']);
        }

        return parent::filter($key, $default, $filter, $options, $deep);
    }
}
<?php

namespace Bolt\Twig\Request;

/**
 * ParameterBag is a container for key/value pairs.
 * Overridden in order to disable certain filters.
 *
 * @author Xiao-Hu Tai <xiao@twokings.nl>
 * @author Fabien Potencier <fabien@symfony.com>
 */
class ServerBag extends \Symfony\Component\HttpFoundation\ServerBag implements \IteratorAggregate, \Countable
{
    use RestrictedFilterTrait;
}