CVE-2017-16754: Incorrect Permission Assignment for Critical Resource (CWE-732)
value: Bolt before 3.3.6 does not properly restrict access to _profiler routes, related to EventListener/ProfilerListener.php and Provider/EventListenerServiceProvider.php.
CWE description: The product specifies permissions for a security-critical resource in a way that allows that resource to be read or modified by unintended actors.
CVSS v3 base score: 5.3
Published: 2017-11-10T02:29Z
Repository: https://github.com/bolt/bolt
Language: PHP
Files changed: src/EventListener/ProfilerListener.php, src/Provider/EventListenerServiceProvider.php
Commit date: 2017-09-26 19:21:19 +0200
Commit message: Request listener to prevent access to profiler routes

Diff: diff --git a/src/EventListener/ProfilerListener.php b/src/EventListener/ProfilerListener.php
new file mode 100644
index 000000000..eee54dc5c
--- /dev/null
+++ b/src/EventListener/ProfilerListener.php
@@ -0,0 +1,74 @@
+<?php
+
+namespace Bolt\EventListener;
+
+use Bolt\AccessControl\Token\Token;
+use Bolt\Request\ProfilerAwareTrait;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\HttpFoundation\Session\SessionInterface;
+use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+use Symfony\Component\HttpKernel\KernelEvents;
+
+/**
+ * Symfony Profiler listener.
+ *
+ * @author Gawain Lynch <gawain.lynch@gmail.com>
+ */
+class ProfilerListener implements EventSubscriberInterface
+{
+    use ProfilerAwareTrait;
+
+    /** @var SessionInterface */
+    private $session;
+    /** @var bool */
+    private $debug;
+    /** @var bool */
+    private $debugLoggedOff;
+
+    /**
+     * Constructor.
+     *
+     * @param SessionInterface $session
+     * @param bool             $debug
+     * @param bool             $debugLoggedOff
+     */
+    public function __construct(SessionInterface $session, $debug, $debugLoggedOff)
+    {
+        $this->session = $session;
+        $this->debug = $debug;
+        $this->debugLoggedOff = $debugLoggedOff;
+    }
+
+    /**
+     * Request listener to prevent access to profiler routes when debugging is
+     * not enabled, or the user is logged off & debugging is not configured to
+     * show when logged off.
+     *
+     * @param GetResponseEvent $event
+     */
+    public function onKernelRequest(GetResponseEvent $event)
+    {
+        $request = $event->getRequest();
+        if (!$this->isProfilerRequest($request)) {
+            return;
+        }
+
+        $token = $this->session->isStarted() ? $this->session->get('authentication') : null;
+        if ($this->debug && ($token instanceof Token || $this->debugLoggedOff)) {
+            return;
+        }
+
+        throw new NotFoundHttpException();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public static function getSubscribedEvents()
+    {
+        return [
+            KernelEvents::REQUEST => ['onKernelRequest'],
+        ];
+    }
+}
diff --git a/src/Provider/EventListenerServiceProvider.php b/src/Provider/EventListenerServiceProvider.php
index 2fa43fcd4..0192670c8 100644
--- a/src/Provider/EventListenerServiceProvider.php
+++ b/src/Provider/EventListenerServiceProvider.php
@@ -128,10 +128,20 @@ class EventListenerServiceProvider implements ServiceProviderInterface
         $app['listener.zone_guesser'] = $app->share(
             function ($app) {
                 return new Listener\ZoneGuesser($app);
             }
         );
+
+        $app['listener.profile'] = $app->share(
+            function ($app) {
+                return new Listener\ProfilerListener(
+                    $app['session'],
+                    $app['debug'],
+                    $app['config']->get('general/debug_show_loggedoff')
+                );
+            }
+        );
     }
 
     public function boot(Application $app)
     {
         /** @var EventDispatcherInterface $dispatcher */
@@ -158,7 +168,9 @@ class EventListenerServiceProvider implements ServiceProviderInterface
         }
 
         if (isset($app['listener.exception']) && !$app['config']->get('general/debug_error_use_symfony')) {
             $dispatcher->addSubscriber($app['listener.exception']);
         }
+
+        $dispatcher->addSubscriber($app['listener.profile']);
     }
 }

Fixed code: <?php

namespace Bolt\EventListener;

use Bolt\AccessControl\Token\Token;
use Bolt\Request\ProfilerAwareTrait;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\KernelEvents;

/**
 * Symfony Profiler listener.
 *
 * @author Gawain Lynch <gawain.lynch@gmail.com>
 */
class ProfilerListener implements EventSubscriberInterface
{
    use ProfilerAwareTrait;

    /** @var SessionInterface */
    private $session;
    /** @var bool */
    private $debug;
    /** @var bool */
    private $debugLoggedOff;

    /**
     * Constructor.
     *
     * @param SessionInterface $session
     * @param bool             $debug
     * @param bool             $debugLoggedOff
     */
    public function __construct(SessionInterface $session, $debug, $debugLoggedOff)
    {
        $this->session = $session;
        $this->debug = $debug;
        $this->debugLoggedOff = $debugLoggedOff;
    }

    /**
     * Request listener to prevent access to profiler routes when debugging is
     * not enabled, or the user is logged off & debugging is not configured to
     * show when logged off.
     *
     * @param GetResponseEvent $event
     */
    public function onKernelRequest(GetResponseEvent $event)
    {
        $request = $event->getRequest();
        if (!$this->isProfilerRequest($request)) {
            return;
        }

        $token = $this->session->isStarted() ? $this->session->get('authentication') : null;
        if ($this->debug && ($token instanceof Token || $this->debugLoggedOff)) {
            return;
        }

        throw new NotFoundHttpException();
    }

    /**
     * {@inheritdoc}
     */
    public static function getSubscribedEvents()
    {
        return [
            KernelEvents::REQUEST => ['onKernelRequest'],
        ];
    }
}

        $app['listener.profile'] = $app->share(
            function ($app) {
                return new Listener\ProfilerListener(
                    $app['session'],
                    $app['debug'],
                    $app['config']->get('general/debug_show_loggedoff')
                );
            }
        );

        $dispatcher->addSubscriber($app['listener.profile']);