Tests für Upload und Ui, strengere CI-Prüfungen
- tests/UploadTest.php prüft den SVG-Filter (Skripte, Event-Handler, javascript:-Verweise) und Upload::isLocal gegen Pfad-Tricks - tests/UiTest.php prüft Versionsstempel, Media-Pfade und die Branding-Overrides inklusive Abweisung ungültiger Farbwerte - PHPStan analysiert jetzt auch includes/Ui.php und includes/Upload.php - CI vergleicht die Sprachdateien (gleiche Schlüsselmenge) und prüft, dass jeder im Code verwendete Schlüssel existiert Dabei aufgefallen und behoben: Upload.php rief __() direkt auf und wäre außerhalb einer Seite mit geladener I18n mit einem Fatal Error abgebrochen; jetzt gibt es einen Fallback auf die deutsche Meldung. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
36e06ac817
commit
0716311ff6
5 changed files with 220 additions and 10 deletions
|
|
@ -16,6 +16,15 @@ class Upload
|
|||
'favicon' => ['ico', 'png', 'svg'],
|
||||
];
|
||||
|
||||
/**
|
||||
* Uebersetzte Meldung – faellt auf Deutsch zurueck, wenn die Klasse
|
||||
* ausserhalb einer Seite mit geladener I18n verwendet wird.
|
||||
*/
|
||||
private static function msg(string $key, string $fallback): string
|
||||
{
|
||||
return function_exists('__') ? __($key) : $fallback;
|
||||
}
|
||||
|
||||
private static function dir(): string
|
||||
{
|
||||
return dirname(__DIR__) . '/uploads';
|
||||
|
|
@ -68,13 +77,13 @@ class Upload
|
|||
return '';
|
||||
}
|
||||
if ($file['error'] !== UPLOAD_ERR_OK) {
|
||||
throw new RuntimeException(__('upload_error_generic'));
|
||||
throw new RuntimeException(self::msg('upload_error_generic', 'Die Datei konnte nicht hochgeladen werden.'));
|
||||
}
|
||||
if (!is_uploaded_file($file['tmp_name'])) {
|
||||
throw new RuntimeException(__('upload_error_generic'));
|
||||
throw new RuntimeException(self::msg('upload_error_generic', 'Die Datei konnte nicht hochgeladen werden.'));
|
||||
}
|
||||
if ($file['size'] > self::MAX_BYTES) {
|
||||
throw new RuntimeException(__('upload_error_size'));
|
||||
throw new RuntimeException(self::msg('upload_error_size', 'Die Datei ist zu groß (maximal 3 MB).'));
|
||||
}
|
||||
|
||||
$allowed = self::ALLOWED[$kind] ?? self::ALLOWED['image'];
|
||||
|
|
@ -83,7 +92,7 @@ class Upload
|
|||
$ext = 'jpg';
|
||||
}
|
||||
if (!in_array($ext, $allowed, true)) {
|
||||
throw new RuntimeException(__('upload_error_type'));
|
||||
throw new RuntimeException(self::msg('upload_error_type', 'Dieser Dateityp wird nicht unterstützt.'));
|
||||
}
|
||||
|
||||
$data = (string)file_get_contents($file['tmp_name']);
|
||||
|
|
@ -93,18 +102,18 @@ class Upload
|
|||
} elseif ($ext !== 'ico') {
|
||||
// Raster: muss als Bild lesbar sein
|
||||
if (@getimagesize($file['tmp_name']) === false) {
|
||||
throw new RuntimeException(__('upload_error_type'));
|
||||
throw new RuntimeException(self::msg('upload_error_type', 'Dieser Dateityp wird nicht unterstützt.'));
|
||||
}
|
||||
}
|
||||
|
||||
if (!self::ensureDir()) {
|
||||
throw new RuntimeException(__('upload_error_dir'));
|
||||
throw new RuntimeException(self::msg('upload_error_dir', 'Der Ordner uploads/ ist nicht beschreibbar.'));
|
||||
}
|
||||
|
||||
$name = bin2hex(random_bytes(8)) . '.' . $ext;
|
||||
$dest = self::dir() . '/' . $name;
|
||||
if (file_put_contents($dest, $data) === false) {
|
||||
throw new RuntimeException(__('upload_error_dir'));
|
||||
throw new RuntimeException(self::msg('upload_error_dir', 'Der Ordner uploads/ ist nicht beschreibbar.'));
|
||||
}
|
||||
@chmod($dest, 0644);
|
||||
|
||||
|
|
@ -118,7 +127,7 @@ class Upload
|
|||
private static function sanitizeSvg(string $svg): string
|
||||
{
|
||||
if (stripos($svg, '<svg') === false) {
|
||||
throw new RuntimeException(__('upload_error_type'));
|
||||
throw new RuntimeException(self::msg('upload_error_type', 'Dieser Dateityp wird nicht unterstützt.'));
|
||||
}
|
||||
|
||||
$svg = preg_replace('#<\s*(script|foreignObject|iframe|embed|object|animate|set)\b[^>]*>.*?<\s*/\s*\1\s*>#is', '', $svg);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue