[ 'DebugKit.Cache' => true, 'DebugKit.Session' => true, 'DebugKit.Request' => true, 'DebugKit.SqlLog' => true, 'DebugKit.Timer' => true, 'DebugKit.Log' => true, 'DebugKit.Variables' => true, 'DebugKit.Environment' => true, 'DebugKit.Include' => true, 'DebugKit.History' => true, 'DebugKit.Routes' => true, 'DebugKit.Packages' => true, 'DebugKit.Mail' => true, 'DebugKit.Deprecations' => true, ], 'forceEnable' => false, 'safeTld' => [] ]; /** * Constructor * * @param \Cake\Event\EventManager $events The event manager to use defaults to the global manager * @param array $config The configuration data for DebugKit. */ public function __construct(EventManager $events, array $config) { $this->setConfig($config); $this->registry = new PanelRegistry($events); } /** * Fetch the PanelRegistry * * @return \DebugKit\Panel\PanelRegistry */ public function registry() { return $this->registry; } /** * Check whether or not debug kit is enabled. * * @return bool */ public function isEnabled() { $enabled = (bool)Configure::read('debug'); if ($enabled && !$this->isSuspiciouslyProduction()) { return true; } $force = $this->getConfig('forceEnable'); if (is_callable($force)) { return $force(); } return $force; } /** * Returns true if this applications is being executed on a domain with a TLD * that is commonly associated with a production environment. * * @return bool */ protected function isSuspiciouslyProduction() { $host = explode('.', parse_url('http://' . env('HTTP_HOST'), PHP_URL_HOST)); $first = current($host); $isIP = is_numeric(implode('', $host)); if (count($host) === 1) { return false; } if ($isIP && in_array($first, ['192', '10', '127'])) { // Accessing the app by private IP, this is safe return false; } $tld = end($host); $safeTopLevelDomains = ['localhost', 'dev', 'invalid', 'test', 'example', 'local']; $safeTopLevelDomains = array_merge($safeTopLevelDomains, (array)$this->getConfig('safeTld')); if (!in_array($tld, $safeTopLevelDomains, true) && !$this->getConfig('forceEnable')) { $host = implode('.', $host); $safeList = implode(', ', $safeTopLevelDomains); Log::warning( "DebugKit is disabling itself as your host `{$host}` " . "is not in the known safe list of top-level-domains ({$safeList}). " . "If you would like to force DebugKit on use the `DebugKit.forceEnable` Configure option." ); return true; } return false; } /** * Get the list of loaded panels * * @return array */ public function loadedPanels() { return $this->registry->loaded(); } /** * Get the a loaded panel * * @param string $name The name of the panel you want to get. * @return \DebugKit\DebugPanel|null The panel or null. */ public function panel($name) { return $this->registry->{$name}; } /** * Load all the panels being used * * @return void */ public function loadPanels() { foreach ($this->getConfig('panels') as $panel => $enabled) { list($panel, $enabled) = (is_numeric($panel)) ? [$enabled, true] : [$panel, $enabled]; if ($enabled) { $this->registry->load($panel); } } } /** * Call the initialize method onl all the loaded panels. * * @return void */ public function initializePanels() { foreach ($this->registry->loaded() as $panel) { $this->registry->{$panel}->initialize(); } } /** * Save the toolbar state. * * @param \Cake\Http\ServerRequest $request The request * @param \Psr\Http\Message\ResponseInterface $response The response * @return null|\DebugKit\Model\Entity\Request Saved request data. */ public function saveData(ServerRequest $request, ResponseInterface $response) { // Skip debugkit requests and requestAction() $path = $request->getUri()->getPath(); if (strpos($path, 'debug_kit') !== false || strpos($path, 'debug-kit') !== false || $request->is('requested') ) { return null; } $data = [ 'url' => $request->getUri()->getPath(), 'content_type' => $response->getHeaderLine('Content-Type'), 'method' => $request->getMethod(), 'status_code' => $response->getStatusCode(), 'requested_at' => $request->getEnv('REQUEST_TIME'), 'panels' => [] ]; /* @var \DebugKit\Model\Table\RequestsTable $requests */ $requests = TableRegistry::get('DebugKit.Requests'); $requests->gc(); $row = $requests->newEntity($data); $row->isNew(true); foreach ($this->registry->loaded() as $name) { $panel = $this->registry->{$name}; try { $content = serialize($panel->data()); } catch (\Exception $e) { $content = serialize([ 'error' => $e->getMessage(), ]); } $row->panels[] = $requests->Panels->newEntity([ 'panel' => $name, 'element' => $panel->elementName(), 'title' => $panel->title(), 'summary' => $panel->summary(), 'content' => $content, ]); } return $requests->save($row); } /** * Reads the modified date of a file in the webroot, and returns the integer * * @return string */ public function getToolbarUrl() { $url = 'js/toolbar.js'; $filePaths = [ str_replace('/', DIRECTORY_SEPARATOR, WWW_ROOT . 'debug_kit/' . $url), str_replace('/', DIRECTORY_SEPARATOR, CorePlugin::path('DebugKit') . 'webroot/' . $url) ]; $url = '/debug_kit/' . $url; foreach ($filePaths as $filePath) { if (file_exists($filePath)) { return $url . '?' . filemtime($filePath); } } return $url; } /** * Injects the JS to build the toolbar. * * The toolbar will only be injected if the response's content type * contains HTML and there is a tag. * * @param \DebugKit\Model\Entity\Request $row The request data to inject. * @param \Psr\Http\Message\ResponseInterface $response The response to augment. * @return \Psr\Http\Message\ResponseInterface The modified response */ public function injectScripts($row, ResponseInterface $response) { $response = $response->withHeader('X-DEBUGKIT-ID', $row->id); if (strpos($response->getHeaderLine('Content-Type'), 'html') === false) { return $response; } $body = $response->getBody(); if (!$body->isSeekable() || !$body->isWritable()) { return $response; } $body->rewind(); $contents = $body->getContents(); $pos = strrpos($contents, ''); if ($pos === false) { return $response; } $url = Router::url('/', true); $script = sprintf( '', $row->id, $url, Router::url($this->getToolbarUrl()) ); $contents = substr($contents, 0, $pos) . $script . substr($contents, $pos); $body->rewind(); $body->write($contents); return $response->withBody($body); } }