diff --git a/docs/en/index.rst b/docs/en/index.rst index 049294e13..225670655 100644 --- a/docs/en/index.rst +++ b/docs/en/index.rst @@ -50,6 +50,12 @@ Configuration // Before loading DebugKit Configure::write('DebugKit.forceEnable', true); + You can also provide a callable:: + + Configure::write('DebugKit.forceEnable', function() { + return $_SERVER['REMOTE_ADDR'] === '192.168.2.182'; + }); + * ``DebugKit.ignorePathsPattern`` - Regex pattern (including delimiter) to ignore paths. DebugKit won't save data for request URLs that match this regex. Defaults to ``null``:: diff --git a/src/Panel/CachePanel.php b/src/Panel/CachePanel.php index eaa7e5c3f..fef295994 100644 --- a/src/Panel/CachePanel.php +++ b/src/Panel/CachePanel.php @@ -55,9 +55,17 @@ public function initialize(): void if (isset($config['className']) && $config['className'] instanceof DebugEngine) { $instance = $config['className']; } elseif (isset($config['className'])) { - Cache::drop($name); - $instance = new DebugEngine($config, $name, $this->logger); + /** @var \Cake\Cache\CacheEngine $engine */ + $engine = Cache::pool($name); + // Unload from the cache registry so that subsequence calls to + // Cache::pool($name) use the new config with DebugEngine instance set below. + Cache::getRegistry()->unload($name); + + $instance = new DebugEngine($engine, $name, $this->logger); + $instance->init(); $config['className'] = $instance; + + Cache::drop($name); Cache::setConfig($name, $config); } if (isset($instance)) { diff --git a/src/ToolbarService.php b/src/ToolbarService.php index b77c272ad..5cf1474f7 100644 --- a/src/ToolbarService.php +++ b/src/ToolbarService.php @@ -365,13 +365,21 @@ public function injectScripts(Request $row, ResponseInterface $response): Respon if ($pos === false) { return $response; } + // Use Router to get the request so that we can see the + // state after other middleware have been applied. + $request = Router::getRequest(); + $nonce = ''; + if ($request && $request->getAttribute('cspScriptNonce')) { + $nonce = sprintf(' nonce="%s"', $request->getAttribute('cspScriptNonce')); + } $url = Router::url('/', true); $script = sprintf( - '', + '', $row->id, $url, - Router::url($this->getToolbarUrl()) + Router::url($this->getToolbarUrl()), + $nonce ); $contents = substr($contents, 0, $pos) . $script . substr($contents, $pos); $body->rewind(); diff --git a/tests/TestCase/Middleware/DebugKitMiddlewareTest.php b/tests/TestCase/Middleware/DebugKitMiddlewareTest.php index b1c97aed8..fad52934f 100644 --- a/tests/TestCase/Middleware/DebugKitMiddlewareTest.php +++ b/tests/TestCase/Middleware/DebugKitMiddlewareTest.php @@ -21,6 +21,7 @@ use Cake\Http\CallbackStream; use Cake\Http\Response; use Cake\Http\ServerRequest; +use Cake\Routing\Router; use Cake\TestSuite\TestCase; use DebugKit\Middleware\DebugKitMiddleware; use Psr\Http\Server\RequestHandlerInterface; @@ -53,7 +54,7 @@ public function setUp(): void parent::setUp(); $connection = ConnectionManager::get('test'); - $this->skipIf($connection->getDriver() instanceof Sqlite, 'Schema insertion/removal breaks SQLite'); + $this->skipIf($connection->getDriver() instanceof Sqlite, 'This test fails in CI with sqlite'); $this->oldConfig = Configure::read('DebugKit'); $this->restore = $GLOBALS['FORCE_DEBUGKIT_TOOLBAR']; $GLOBALS['FORCE_DEBUGKIT_TOOLBAR'] = true; @@ -135,6 +136,39 @@ public function testInvokeSaveData() $this->assertTextEquals($expected, $body); } + /** + * Ensure data is saved for HTML requests + * + * @return void + */ + public function testInvokeInjectCspNonce() + { + $request = new ServerRequest([ + 'url' => '/articles', + 'environment' => ['REQUEST_METHOD' => 'GET'], + ]); + $request = $request->withAttribute('cspScriptNonce', 'csp-nonce'); + Router::setRequest($request); + + $response = new Response([ + 'statusCode' => 200, + 'type' => 'text/html', + 'body' => 'test

some text

', + ]); + + $handler = $this->handler(); + $handler->expects($this->once()) + ->method('handle') + ->willReturn($response); + + $middleware = new DebugKitMiddleware(); + $response = $middleware->process($request, $handler); + $this->assertInstanceOf(Response::class, $response, 'Should return the response'); + + $body = (string)$response->getBody(); + $this->assertStringContainsString('nonce="csp-nonce"', $body); + } + /** * Ensure that streaming results are tracked, but not modified. * diff --git a/tests/TestCase/ToolbarServiceTest.php b/tests/TestCase/ToolbarServiceTest.php index 47083b41c..d9283ff40 100644 --- a/tests/TestCase/ToolbarServiceTest.php +++ b/tests/TestCase/ToolbarServiceTest.php @@ -22,6 +22,7 @@ use Cake\Http\Response; use Cake\Http\ServerRequest as Request; use Cake\Log\Log; +use Cake\Routing\Router; use Cake\TestSuite\TestCase; use DebugKit\Model\Entity\Request as RequestEntity; use DebugKit\ToolbarService; @@ -294,6 +295,7 @@ public function testInjectScriptsLastBodyTag() 'url' => '/articles', 'environment' => ['REQUEST_METHOD' => 'GET'], ]); + Router::setRequest($request); $response = new Response([ 'statusCode' => 200, 'type' => 'text/html', @@ -305,7 +307,7 @@ public function testInjectScriptsLastBodyTag() $row = $bar->saveData($request, $response); $response = $bar->injectScripts($row, $response); - $timeStamp = filemtime(Plugin::path('DebugKit') . 'webroot' . DS . 'js' . DS . 'main.js'); + $timeStamp = filemtime(Plugin::path('DebugKit') . 'webroot' . DS . 'js' . DS . 'inject-iframe.js'); $expected = 'test

some text

' . '