feature #40441 [WebProfilerBundle] Disable CSP if dumper was used (monojp)
This PR was squashed before being merged into the 5.3-dev branch.
Discussion
----------
[WebProfilerBundle] Disable CSP if dumper was used
| Q | A
| ------------- | ---
| Branch? | 5.x for features
| Bug fix? | no
| New feature? | no
| Deprecations? | no
| Tickets | Fix #29084
| License | MIT
| Doc PR | -
Disables a configured Content Security Policy if the dumper was used to avoid breaking the toolbar. This is a less invasive alternative to #29155 which fixes #29084 (there is a project with a test case linked there).
Commits
-------
5dc2637263
[WebProfilerBundle] Disable CSP if dumper was used
This commit is contained in:
commit
bbb4d9f26e
@ -16,6 +16,7 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\Session\Flash\AutoExpireFlashBag;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DumpDataCollector;
|
||||
use Symfony\Component\HttpKernel\Event\ResponseEvent;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
@ -44,8 +45,9 @@ class WebDebugToolbarListener implements EventSubscriberInterface
|
||||
protected $mode;
|
||||
protected $excludedAjaxPaths;
|
||||
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->urlGenerator = $urlGenerator;
|
||||
@ -53,6 +55,7 @@ class WebDebugToolbarListener implements EventSubscriberInterface
|
||||
$this->mode = $mode;
|
||||
$this->excludedAjaxPaths = $excludedAjaxPaths;
|
||||
$this->cspHandler = $cspHandler;
|
||||
$this->dumpDataCollector = $dumpDataCollector;
|
||||
}
|
||||
|
||||
public function isEnabled(): bool
|
||||
@ -89,7 +92,14 @@ class WebDebugToolbarListener implements EventSubscriberInterface
|
||||
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
|
||||
if ($request->isXmlHttpRequest()) {
|
||||
|
@ -24,6 +24,7 @@ return static function (ContainerConfigurator $container) {
|
||||
service('router')->ignoreOnInvalid(),
|
||||
abstract_arg('paths that should be excluded from the AJAX requests shown in the toolbar'),
|
||||
service('web_profiler.csp.handler'),
|
||||
service('data_collector.dump')->ignoreOnInvalid(),
|
||||
])
|
||||
->tag('kernel.event_subscriber')
|
||||
;
|
||||
|
@ -20,6 +20,7 @@ use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer;
|
||||
use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DumpDataCollector;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
|
||||
class WebProfilerExtensionTest extends TestCase
|
||||
@ -55,6 +56,7 @@ class WebProfilerExtensionTest extends TestCase
|
||||
$this->kernel = $this->createMock(KernelInterface::class);
|
||||
|
||||
$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('event_dispatcher', EventDispatcher::class)->setPublic(true);
|
||||
$this->container->register('router', $this->getMockClass('Symfony\\Component\\Routing\\RouterInterface'))->setPublic(true);
|
||||
|
@ -12,11 +12,13 @@
|
||||
namespace Symfony\Bundle\WebProfilerBundle\Tests\EventListener;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Bundle\WebProfilerBundle\Csp\ContentSecurityPolicyHandler;
|
||||
use Symfony\Bundle\WebProfilerBundle\EventListener\WebDebugToolbarListener;
|
||||
use Symfony\Component\HttpFoundation\HeaderBag;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\Session\Session;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DumpDataCollector;
|
||||
use Symfony\Component\HttpKernel\Event\ResponseEvent;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
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'));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
$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\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DumpDataCollector;
|
||||
use Symfony\Component\HttpKernel\Kernel;
|
||||
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
|
||||
|
||||
@ -65,6 +66,7 @@ class WebProfilerBundleKernel extends Kernel
|
||||
|
||||
protected function build(ContainerBuilder $container)
|
||||
{
|
||||
$container->register('data_collector.dump', DumpDataCollector::class);
|
||||
$container->register('logger', NullLogger::class);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user