78 lines
3.1 KiB
PHP
78 lines
3.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/**
|
|
* Test 68: Produktions-Härtung der PHP-Fehlerausgabe (Issue #142).
|
|
* Prüft, dass display_errors deaktiviert ist, die Ini im Image landet und
|
|
* public/index.php Fehler zentral abfängt statt Details an den Client zu senden.
|
|
*/
|
|
class ProductionErrorHardeningTest extends TestCase
|
|
{
|
|
protected string $testName = 'Test 68: display_errors aus & zentraler Error-Handler ohne Detail-Leak';
|
|
|
|
public function run(): void
|
|
{
|
|
$baseDir = dirname(__DIR__);
|
|
|
|
$iniPath = $baseDir . '/php/error-handling.ini';
|
|
$this->assert(is_file($iniPath), 'php/error-handling.ini existiert');
|
|
$ini = file_get_contents($iniPath) ?: '';
|
|
$this->assert(
|
|
(bool) preg_match('/^\s*display_errors\s*=\s*Off\s*$/mi', $ini),
|
|
'display_errors ist in error-handling.ini auf Off gesetzt'
|
|
);
|
|
$this->assert(
|
|
(bool) preg_match('/^\s*log_errors\s*=\s*On\s*$/mi', $ini),
|
|
'log_errors ist in error-handling.ini auf On gesetzt'
|
|
);
|
|
|
|
$dockerfile = file_get_contents($baseDir . '/Dockerfile') ?: '';
|
|
$this->assert(
|
|
str_contains($dockerfile, 'php/error-handling.ini') && str_contains($dockerfile, '/usr/local/etc/php/conf.d/'),
|
|
'Dockerfile kopiert error-handling.ini in den PHP conf.d-Ordner'
|
|
);
|
|
|
|
$index = file_get_contents($baseDir . '/public/index.php') ?: '';
|
|
$this->assert(
|
|
str_contains($index, 'set_exception_handler'),
|
|
'index.php registriert einen globalen Exception-Handler'
|
|
);
|
|
$this->assert(
|
|
str_contains($index, 'set_error_handler'),
|
|
'index.php registriert einen globalen Error-Handler'
|
|
);
|
|
$this->assert(
|
|
str_contains($index, 'register_shutdown_function'),
|
|
'index.php registriert einen Shutdown-Handler für Fatal Errors'
|
|
);
|
|
|
|
// Der Handler muss vor dem Autoload/Routing stehen, damit auch frühe Fehler abgefangen werden.
|
|
$handlerPos = strpos($index, 'set_exception_handler');
|
|
$autoloadPos = strpos($index, "require BASE_DIR . '/vendor/autoload.php'");
|
|
$this->assert(
|
|
$handlerPos !== false && $autoloadPos !== false && $handlerPos < $autoloadPos,
|
|
'Exception-Handler wird vor dem Autoload registriert'
|
|
);
|
|
|
|
// Sicherstellen, dass im an den Client gesendeten HTML keine Exception-Details landen.
|
|
$exceptionHandlerBlock = substr($index, $handlerPos, 400);
|
|
foreach (['getMessage()', 'getTraceAsString()', 'getFile()', '$e->'] as $forbidden) {
|
|
$this->assert(
|
|
!str_contains($this->extractEchoBlock($index), $forbidden),
|
|
"Client-Ausgabe enthält keinen Verweis auf {$forbidden}"
|
|
);
|
|
}
|
|
unset($exceptionHandlerBlock);
|
|
}
|
|
|
|
private function extractEchoBlock(string $index): string
|
|
{
|
|
$start = strpos($index, "echo '<!DOCTYPE html>");
|
|
$end = strpos($index, "};", $start !== false ? $start : 0);
|
|
if ($start === false || $end === false) {
|
|
return '';
|
|
}
|
|
return substr($index, $start, $end - $start);
|
|
}
|
|
}
|