Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,39 @@ You don't need to have a PHP environment with composer installed.
* Pimcore Studio, using the credentials from `.env` (default `admin` / `admin`):
<http://localhost/pimcore-studio>

## Symfony profiler (dev environment)

In the `dev` environment (`APP_ENV=dev`) the skeleton ships with two safe defaults for the Symfony web profiler:

* The **debug toolbar is off**.
* The **`/_profiler/phpinfo` page is blocked**. It prints `phpinfo()` - including environment variables and secrets - to
anyone who can reach the instance, and Symfony has no setting to switch it off separately.

The profiler itself keeps collecting data and stays available at `/_profiler`.

Only activate the following on instances that untrusted users cannot reach, and never in production (use `APP_ENV=prod`
there). After each change, clear the cache - the change does not show up before that - and reload the page:
`bin/console cache:clear` (with Docker: `docker compose exec php bin/console cache:clear`).

### Activate the toolbar

Set `web_profiler.toolbar` to `true` in `config/packages/dev/config.yaml`:

```yaml
web_profiler:
toolbar: true
```

### Activate the `/_profiler/phpinfo` page

Delete the `_profiler_phpinfo` route (including its comment) at the end of `config/routes/dev/routes.yaml`, so that only
the `_pimcore_dev` import remains:

```yaml
_pimcore_dev:
resource: "@PimcoreCoreBundle/config/routing_dev.yaml"
```

## Pimcore Platform Version
By default, Pimcore Platform Version is added as a dependency which ensures installation of compatible and in combination
with each other tested versions of additional Pimcore modules. More information about the Platform Version can be found in the
Expand Down
5 changes: 5 additions & 0 deletions config/packages/dev/config.yaml
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
imports:
- { resource: ../../config.yaml }

# The Symfony debug toolbar is off by default, so a dev instance that is reachable from the network does not
# advertise the profiler on every page. Set it to `true` locally when you want it.
web_profiler:
toolbar: false
9 changes: 9 additions & 0 deletions config/routes/dev/routes.yaml
Original file line number Diff line number Diff line change
@@ -1,2 +1,11 @@
_pimcore_dev:
resource: "@PimcoreCoreBundle/config/routing_dev.yaml"

# Symfony's profiler route `/_profiler/phpinfo` prints phpinfo() - including environment variables and secrets - to
# anyone who can reach the application, and it is not affected by `web_profiler.toolbar`. Redefining the route
# under the same name replaces it with one that never matches, so the URL falls through to the profiler's token
# route, which only reports an unknown token, while the rest of the profiler keeps working. Delete this route to
# get the page back.
_profiler_phpinfo:
path: /_profiler/phpinfo
condition: "false"
130 changes: 130 additions & 0 deletions tests/Functional/Dev/ProfilerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
<?php
declare(strict_types=1);

namespace App\Tests\Functional\Dev;

use App\Kernel;
use Pimcore\Test\KernelTestCase;
use Symfony\Bundle\WebProfilerBundle\EventListener\WebDebugToolbarListener;
use Symfony\Component\HttpFoundation\Request;

/**
* Covers the shipped dev-environment profiler defaults (config/packages/dev, config/routes/dev).
*/
class ProfilerTest extends KernelTestCase
{
/**
* Original DATABASE_URL of each environment store while it is overridden; null means the store had no entry.
*
* @var array{server: array{value: mixed}|null, env: array{value: mixed}|null, process: array{value: string}|null}|null
*/
private ?array $originalDatabaseUrl = null;

protected static function getKernelClass(): string
{
return Kernel::class;
}

protected function setUp(): void
{
parent::setUp();

$this->useTestDatabase();

// The profiler is only available in the dev environment, so boot that one instead of the test environment.
self::bootKernel(['environment' => 'dev', 'debug' => true]);
Comment thread
robertSt7 marked this conversation as resolved.
Resolved
}

protected function tearDown(): void
{
parent::tearDown();

$this->restoreDatabaseUrl();
}

public function testPhpinfoRouteIsNotMatched(): void
{
$match = self::$kernel->getContainer()->get('router')->match('/_profiler/phpinfo');

// The URL must fall through to the profiler's token route instead of reaching the phpinfo action.
self::assertNotSame('_profiler_phpinfo', $match['_route']);
self::assertSame('_profiler', $match['_route']);
self::assertSame('phpinfo', $match['token']);
}

public function testPhpinfoIsNotServed(): void
{
$response = self::$kernel->handle(Request::create('/_profiler/phpinfo'));
$content = (string) $response->getContent();

// The profiler's token route answers instead, reporting an unknown token.
self::assertStringNotContainsString('PHP Version', $content);
self::assertStringNotContainsString('PHP Credits', $content);
self::assertStringContainsString('phpinfo', $content);
}

public function testProfilerRoutesStillWork(): void
{
$router = self::$kernel->getContainer()->get('router');

// Only phpinfo is blocked: the profiler itself and its links to the phpinfo route keep working.
self::assertSame('_profiler_search_results', $router->match('/_profiler/abc123/search/results')['_route']);
self::assertSame('/_profiler/phpinfo', $router->generate('_profiler_phpinfo'));
}

public function testToolbarIsDisabledByDefault(): void
{
$container = self::$kernel->getContainer();

// The parameter is only registered while the toolbar (or redirect interception) is enabled.
$enabled = $container->hasParameter('web_profiler.debug_toolbar.mode')
&& WebDebugToolbarListener::ENABLED === $container->getParameter('web_profiler.debug_toolbar.mode');

self::assertFalse($enabled);
}

/**
* The dev environment does not apply the test database override of config/packages/test, so point the
* default connection at the test database explicitly instead of the development one.
*/
private function useTestDatabase(): void
{
$dsn = $_SERVER['PIMCORE_TEST_DB_DSN'] ?? $_ENV['PIMCORE_TEST_DB_DSN'] ?? getenv('PIMCORE_TEST_DB_DSN');
if (!is_string($dsn) || '' === $dsn) {
return;
}

$processValue = getenv('DATABASE_URL');
$this->originalDatabaseUrl = [
'server' => array_key_exists('DATABASE_URL', $_SERVER) ? ['value' => $_SERVER['DATABASE_URL']] : null,
'env' => array_key_exists('DATABASE_URL', $_ENV) ? ['value' => $_ENV['DATABASE_URL']] : null,
'process' => false === $processValue ? null : ['value' => $processValue],
];

$_SERVER['DATABASE_URL'] = $_ENV['DATABASE_URL'] = $dsn;
putenv('DATABASE_URL=' . $dsn);
}

private function restoreDatabaseUrl(): void
{
if (null === $this->originalDatabaseUrl) {
return;
}

if (null === $this->originalDatabaseUrl['server']) {
unset($_SERVER['DATABASE_URL']);
} else {
$_SERVER['DATABASE_URL'] = $this->originalDatabaseUrl['server']['value'];
}

if (null === $this->originalDatabaseUrl['env']) {
unset($_ENV['DATABASE_URL']);
} else {
$_ENV['DATABASE_URL'] = $this->originalDatabaseUrl['env']['value'];
}

putenv('DATABASE_URL' . (null === $this->originalDatabaseUrl['process'] ? '' : '=' . $this->originalDatabaseUrl['process']['value']));

$this->originalDatabaseUrl = null;
}
}
Loading