[WebProfilerBundle] Disable CSP if dumper was used
This commit is contained in:
parent
c06a76c384
commit
5dc2637263
@ -16,6 +16,7 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
|||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\HttpFoundation\Session\Flash\AutoExpireFlashBag;
|
use Symfony\Component\HttpFoundation\Session\Flash\AutoExpireFlashBag;
|
||||||
|
use Symfony\Component\HttpKernel\DataCollector\DumpDataCollector;
|
||||||
use Symfony\Component\HttpKernel\Event\ResponseEvent;
|
use Symfony\Component\HttpKernel\Event\ResponseEvent;
|
||||||
use Symfony\Component\HttpKernel\KernelEvents;
|
use Symfony\Component\HttpKernel\KernelEvents;
|
||||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||||
@ -44,8 +45,9 @@ class WebDebugToolbarListener implements EventSubscriberInterface
|
|||||||
protected $mode;
|
protected $mode;
|
||||||
protected $excludedAjaxPaths;
|
protected $excludedAjaxPaths;
|
||||||
private $cspHandler;
|
private $cspHandler;
|
||||||
|
private $dumpDataCollector;
|
||||||
|
|
||||||
public function __construct(Environment $twig, bool $interceptRedirects = false, int $mode = self::ENABLED, UrlGeneratorInterface $urlGenerator = null, string $excludedAjaxPaths = '^/bundles|^/_wdt', ContentSecurityPolicyHandler $cspHandler = null)
|
public function __construct(Environment $twig, bool $interceptRedirects = false, int $mode = self::ENABLED, UrlGeneratorInterface $urlGenerator = null, string $excludedAjaxPaths = '^/bundles|^/_wdt', ContentSecurityPolicyHandler $cspHandler = null, DumpDataCollector $dumpDataCollector = null)
|
||||||
{
|
{
|
||||||
$this->twig = $twig;
|
$this->twig = $twig;
|
||||||
$this->urlGenerator = $urlGenerator;
|
$this->urlGenerator = $urlGenerator;
|
||||||
@ -53,6 +55,7 @@ class WebDebugToolbarListener implements EventSubscriberInterface
|
|||||||
$this->mode = $mode;
|
$this->mode = $mode;
|
||||||
$this->excludedAjaxPaths = $excludedAjaxPaths;
|
$this->excludedAjaxPaths = $excludedAjaxPaths;
|
||||||
$this->cspHandler = $cspHandler;
|
$this->cspHandler = $cspHandler;
|
||||||
|
$this->dumpDataCollector = $dumpDataCollector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isEnabled(): bool
|
public function isEnabled(): bool
|
||||||
@ -89,7 +92,14 @@ class WebDebugToolbarListener implements EventSubscriberInterface
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$nonces = $this->cspHandler ? $this->cspHandler->updateResponseHeaders($request, $response) : [];
|
$nonces = [];
|
||||||
|
if ($this->cspHandler) {
|
||||||
|
if ($this->dumpDataCollector && $this->dumpDataCollector->getDumpsCount() > 0) {
|
||||||
|
$this->cspHandler->disableCsp();
|
||||||
|
}
|
||||||
|
|
||||||
|
$nonces = $this->cspHandler->updateResponseHeaders($request, $response);
|
||||||
|
}
|
||||||
|
|
||||||
// do not capture redirects or modify XML HTTP Requests
|
// do not capture redirects or modify XML HTTP Requests
|
||||||
if ($request->isXmlHttpRequest()) {
|
if ($request->isXmlHttpRequest()) {
|
||||||
|
@ -24,6 +24,7 @@ return static function (ContainerConfigurator $container) {
|
|||||||
service('router')->ignoreOnInvalid(),
|
service('router')->ignoreOnInvalid(),
|
||||||
abstract_arg('paths that should be excluded from the AJAX requests shown in the toolbar'),
|
abstract_arg('paths that should be excluded from the AJAX requests shown in the toolbar'),
|
||||||
service('web_profiler.csp.handler'),
|
service('web_profiler.csp.handler'),
|
||||||
|
service('data_collector.dump')->ignoreOnInvalid(),
|
||||||
])
|
])
|
||||||
->tag('kernel.event_subscriber')
|
->tag('kernel.event_subscriber')
|
||||||
;
|
;
|
||||||
|
@ -20,6 +20,7 @@ use Symfony\Component\DependencyInjection\Reference;
|
|||||||
use Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer;
|
use Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer;
|
||||||
use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass;
|
use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass;
|
||||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||||
|
use Symfony\Component\HttpKernel\DataCollector\DumpDataCollector;
|
||||||
use Symfony\Component\HttpKernel\KernelInterface;
|
use Symfony\Component\HttpKernel\KernelInterface;
|
||||||
|
|
||||||
class WebProfilerExtensionTest extends TestCase
|
class WebProfilerExtensionTest extends TestCase
|
||||||
@ -55,6 +56,7 @@ class WebProfilerExtensionTest extends TestCase
|
|||||||
$this->kernel = $this->createMock(KernelInterface::class);
|
$this->kernel = $this->createMock(KernelInterface::class);
|
||||||
|
|
||||||
$this->container = new ContainerBuilder();
|
$this->container = new ContainerBuilder();
|
||||||
|
$this->container->register('data_collector.dump', DumpDataCollector::class)->setPublic(true);
|
||||||
$this->container->register('error_handler.error_renderer.html', HtmlErrorRenderer::class)->setPublic(true);
|
$this->container->register('error_handler.error_renderer.html', HtmlErrorRenderer::class)->setPublic(true);
|
||||||
$this->container->register('event_dispatcher', EventDispatcher::class)->setPublic(true);
|
$this->container->register('event_dispatcher', EventDispatcher::class)->setPublic(true);
|
||||||
$this->container->register('router', $this->getMockClass('Symfony\\Component\\Routing\\RouterInterface'))->setPublic(true);
|
$this->container->register('router', $this->getMockClass('Symfony\\Component\\Routing\\RouterInterface'))->setPublic(true);
|
||||||
|
@ -12,11 +12,13 @@
|
|||||||
namespace Symfony\Bundle\WebProfilerBundle\Tests\EventListener;
|
namespace Symfony\Bundle\WebProfilerBundle\Tests\EventListener;
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Bundle\WebProfilerBundle\Csp\ContentSecurityPolicyHandler;
|
||||||
use Symfony\Bundle\WebProfilerBundle\EventListener\WebDebugToolbarListener;
|
use Symfony\Bundle\WebProfilerBundle\EventListener\WebDebugToolbarListener;
|
||||||
use Symfony\Component\HttpFoundation\HeaderBag;
|
use Symfony\Component\HttpFoundation\HeaderBag;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\HttpFoundation\Session\Session;
|
use Symfony\Component\HttpFoundation\Session\Session;
|
||||||
|
use Symfony\Component\HttpKernel\DataCollector\DumpDataCollector;
|
||||||
use Symfony\Component\HttpKernel\Event\ResponseEvent;
|
use Symfony\Component\HttpKernel\Event\ResponseEvent;
|
||||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||||
use Symfony\Component\HttpKernel\Kernel;
|
use Symfony\Component\HttpKernel\Kernel;
|
||||||
@ -300,6 +302,48 @@ class WebDebugToolbarListenerTest extends TestCase
|
|||||||
$this->assertEquals('Exception: This multiline tabbed text should come out on a single plain line', $response->headers->get('X-Debug-Error'));
|
$this->assertEquals('Exception: This multiline tabbed text should come out on a single plain line', $response->headers->get('X-Debug-Error'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testCspIsDisabledIfDumperWasUsed()
|
||||||
|
{
|
||||||
|
$response = new Response('<html><head></head><body></body></html>');
|
||||||
|
$response->headers->set('X-Debug-Token', 'xxxxxxxx');
|
||||||
|
|
||||||
|
$event = new ResponseEvent($this->createMock(Kernel::class), $this->getRequestMock(), HttpKernelInterface::MASTER_REQUEST, $response);
|
||||||
|
|
||||||
|
$cspHandler = $this->createMock(ContentSecurityPolicyHandler::class);
|
||||||
|
$cspHandler->expects($this->once())
|
||||||
|
->method('disableCsp');
|
||||||
|
$dumpDataCollector = $this->createMock(DumpDataCollector::class);
|
||||||
|
$dumpDataCollector->expects($this->once())
|
||||||
|
->method('getDumpsCount')
|
||||||
|
->willReturn(1);
|
||||||
|
|
||||||
|
$listener = new WebDebugToolbarListener($this->getTwigMock(), false, WebDebugToolbarListener::ENABLED, null, '', $cspHandler, $dumpDataCollector);
|
||||||
|
$listener->onKernelResponse($event);
|
||||||
|
|
||||||
|
$this->assertEquals("<html><head></head><body>\nWDT\n</body></html>", $response->getContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCspIsKeptEnabledIfDumperWasNotUsed()
|
||||||
|
{
|
||||||
|
$response = new Response('<html><head></head><body></body></html>');
|
||||||
|
$response->headers->set('X-Debug-Token', 'xxxxxxxx');
|
||||||
|
|
||||||
|
$event = new ResponseEvent($this->createMock(Kernel::class), $this->getRequestMock(), HttpKernelInterface::MASTER_REQUEST, $response);
|
||||||
|
|
||||||
|
$cspHandler = $this->createMock(ContentSecurityPolicyHandler::class);
|
||||||
|
$cspHandler->expects($this->never())
|
||||||
|
->method('disableCsp');
|
||||||
|
$dumpDataCollector = $this->createMock(DumpDataCollector::class);
|
||||||
|
$dumpDataCollector->expects($this->once())
|
||||||
|
->method('getDumpsCount')
|
||||||
|
->willReturn(0);
|
||||||
|
|
||||||
|
$listener = new WebDebugToolbarListener($this->getTwigMock(), false, WebDebugToolbarListener::ENABLED, null, '', $cspHandler, $dumpDataCollector);
|
||||||
|
$listener->onKernelResponse($event);
|
||||||
|
|
||||||
|
$this->assertEquals("<html><head></head><body>\nWDT\n</body></html>", $response->getContent());
|
||||||
|
}
|
||||||
|
|
||||||
protected function getRequestMock($isXmlHttpRequest = false, $requestFormat = 'html', $hasSession = true)
|
protected function getRequestMock($isXmlHttpRequest = false, $requestFormat = 'html', $hasSession = true)
|
||||||
{
|
{
|
||||||
$request = $this->getMockBuilder(Request::class)->setMethods(['getSession', 'isXmlHttpRequest', 'getRequestFormat'])->disableOriginalConstructor()->getMock();
|
$request = $this->getMockBuilder(Request::class)->setMethods(['getSession', 'isXmlHttpRequest', 'getRequestFormat'])->disableOriginalConstructor()->getMock();
|
||||||
|
@ -10,6 +10,7 @@ use Symfony\Bundle\WebProfilerBundle\WebProfilerBundle;
|
|||||||
use Symfony\Component\Config\Loader\LoaderInterface;
|
use Symfony\Component\Config\Loader\LoaderInterface;
|
||||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\HttpKernel\DataCollector\DumpDataCollector;
|
||||||
use Symfony\Component\HttpKernel\Kernel;
|
use Symfony\Component\HttpKernel\Kernel;
|
||||||
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
|
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
|
||||||
|
|
||||||
@ -65,6 +66,7 @@ class WebProfilerBundleKernel extends Kernel
|
|||||||
|
|
||||||
protected function build(ContainerBuilder $container)
|
protected function build(ContainerBuilder $container)
|
||||||
{
|
{
|
||||||
|
$container->register('data_collector.dump', DumpDataCollector::class);
|
||||||
$container->register('logger', NullLogger::class);
|
$container->register('logger', NullLogger::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user