fix: Imagick in der Betriebsüberwachung, Scanner-Anfragen an Livewire als 400, abgebrochene Alpine-Übergänge nicht mehr melden
Auf Live fehlte die PHP-Erweiterung imagick: Herkunftsprüfung und
KI-Vorab-Check der Bilder scheiterten still und ließen jedes Bild durch.
AdminSystemHealth zeigt jetzt die Zeile „PHP-Erweiterung Imagick“ (rot bei
eingeschaltetem Vorab-Check, sonst gelb).
GlitchTip server-pressekonto, Issues 38 und 39: gebaute Livewire-Anfragen
setzen gesperrte Properties (portal/language im Meldungs-Feed und in der
Suche) oder schicken Uploads an Komponenten ohne Upload. Livewire weist sie
korrekt ab; statt 500 bzw. 419 (das als „Seite abgelaufen“ gemeldet wurde)
antworten sie jetzt mit 400 und werden nicht mehr gemeldet.
Browser (Issue 18): Alpine bricht x-transition-Übergänge mit
{isFromCancelledTransition: true} ab — keine Fehlermeldung mehr.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
65086cafd2
commit
bbc2b6048f
5 changed files with 90 additions and 0 deletions
|
|
@ -238,6 +238,19 @@ class AdminSystemHealth
|
||||||
: 'SESSION_SECURE_COOKIE ist aus'.($isLocal ? ' (lokal ok).' : ' — in https-Umgebungen aktivieren.'),
|
: 'SESSION_SECURE_COOKIE ist aus'.($isLocal ? ' (lokal ok).' : ' — in https-Umgebungen aktivieren.'),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Imagick trägt Herkunftsprüfung und KI-Vorab-Check der Bilder. Fehlt
|
||||||
|
// die Erweiterung, scheitern beide still (Bilder werden durchgelassen)
|
||||||
|
// — so geschehen auf Live bis 08.10.2026.
|
||||||
|
$precheckEnabled = (bool) config('images.precheck.enabled');
|
||||||
|
$checks[] = [
|
||||||
|
'key' => 'imagick',
|
||||||
|
'label' => 'PHP-Erweiterung Imagick',
|
||||||
|
'status' => $this->imagickAvailable() ? self::StatusOk : ($precheckEnabled ? self::StatusCritical : self::StatusWarn),
|
||||||
|
'detail' => $this->imagickAvailable()
|
||||||
|
? 'Imagick ist geladen (Herkunftsprüfung und KI-Vorab-Check der Bilder).'
|
||||||
|
: 'Imagick fehlt — Herkunftsprüfung'.($precheckEnabled ? ' und KI-Vorab-Check' : '').' der Bilder laufen ins Leere. Auf dem Server php8.4-imagick installieren und PHP-FPM neu laden.',
|
||||||
|
];
|
||||||
|
|
||||||
// Herkunftsprüfung der Bild-Uploads (Bildkennzeichnung E.7): Fehler
|
// Herkunftsprüfung der Bild-Uploads (Bildkennzeichnung E.7): Fehler
|
||||||
// der letzten sieben Tage zählen; Zustand D im Formular blockiert
|
// der letzten sieben Tage zählen; Zustand D im Formular blockiert
|
||||||
// nichts, muss aber sichtbar bleiben.
|
// nichts, muss aber sichtbar bleiben.
|
||||||
|
|
@ -341,6 +354,11 @@ class AdminSystemHealth
|
||||||
return $checks;
|
return $checks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function imagickAvailable(): bool
|
||||||
|
{
|
||||||
|
return class_exists(\Imagick::class);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<string, mixed> $queue
|
* @param array<string, mixed> $queue
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,11 @@ use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
use Laravel\Sanctum\Http\Middleware\CheckAbilities;
|
use Laravel\Sanctum\Http\Middleware\CheckAbilities;
|
||||||
use Laravel\Sanctum\Http\Middleware\CheckForAnyAbility;
|
use Laravel\Sanctum\Http\Middleware\CheckForAnyAbility;
|
||||||
|
use Livewire\Features\SupportFileUploads\MissingFileUploadsTraitException;
|
||||||
|
use Livewire\Features\SupportLockedProperties\CannotUpdateLockedPropertyException;
|
||||||
use Sentry\Laravel\Integration;
|
use Sentry\Laravel\Integration;
|
||||||
use Symfony\Component\Console\Exception\CommandNotFoundException;
|
use Symfony\Component\Console\Exception\CommandNotFoundException;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||||
|
|
||||||
return Application::configure(basePath: dirname(__DIR__))
|
return Application::configure(basePath: dirname(__DIR__))
|
||||||
->withRouting(
|
->withRouting(
|
||||||
|
|
@ -177,4 +180,18 @@ return Application::configure(basePath: dirname(__DIR__))
|
||||||
// Vertippte artisan-Befehle sind kein Anwendungsfehler (GlitchTip
|
// Vertippte artisan-Befehle sind kein Anwendungsfehler (GlitchTip
|
||||||
// server-pressekonto, Issue 9: „config:cachels“ auf Live).
|
// server-pressekonto, Issue 9: „config:cachels“ auf Live).
|
||||||
$exceptions->dontReport(CommandNotFoundException::class);
|
$exceptions->dontReport(CommandNotFoundException::class);
|
||||||
|
|
||||||
|
// Manipulierte Livewire-Anfragen (Scanner, die gesperrte Properties
|
||||||
|
// setzen oder Uploads an Komponenten ohne Upload schicken) als 400
|
||||||
|
// beantworten statt als 500 bzw. 419 — Livewire weist sie korrekt ab,
|
||||||
|
// sie sind kein Anwendungsfehler und kein abgelaufenes Formular
|
||||||
|
// (GlitchTip server-pressekonto, Issues 38 und 39, 08.10.2026).
|
||||||
|
$exceptions->map(
|
||||||
|
CannotUpdateLockedPropertyException::class,
|
||||||
|
fn (CannotUpdateLockedPropertyException $e): BadRequestHttpException => new BadRequestHttpException('Ungültige Livewire-Anfrage.', $e),
|
||||||
|
);
|
||||||
|
$exceptions->map(
|
||||||
|
MissingFileUploadsTraitException::class,
|
||||||
|
fn (MissingFileUploadsTraitException $e): BadRequestHttpException => new BadRequestHttpException('Ungültige Livewire-Anfrage.', $e),
|
||||||
|
);
|
||||||
})->create();
|
})->create();
|
||||||
|
|
|
||||||
|
|
@ -123,6 +123,12 @@ if (config?.dsn) {
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener('unhandledrejection', (event) => {
|
window.addEventListener('unhandledrejection', (event) => {
|
||||||
|
// Alpine bricht laufende x-transition-Übergänge mit diesem Objekt ab
|
||||||
|
// (GlitchTip browser-pressekonto, Issue 18) — kein Fehler.
|
||||||
|
if (event.reason?.isFromCancelledTransition === true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
capture(rejectionToError(event.reason));
|
capture(rejectionToError(event.reason));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -124,3 +124,28 @@ test('snapshot flags pending media privatization', function () {
|
||||||
expect($checks['media_privatization']['status'])->toBe('warn')
|
expect($checks['media_privatization']['status'])->toBe('warn')
|
||||||
->and($checks['media_privatization']['detail'])->toContain('1');
|
->and($checks['media_privatization']['detail'])->toContain('1');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('snapshot flags a missing imagick extension', function (bool $precheckEnabled, string $status) {
|
||||||
|
config()->set('images.precheck.enabled', $precheckEnabled);
|
||||||
|
app()->bind(AdminSystemHealth::class, fn () => new class extends AdminSystemHealth
|
||||||
|
{
|
||||||
|
protected function imagickAvailable(): bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$checks = collect(app(AdminSystemHealth::class)->snapshot()['checks'])->keyBy('key');
|
||||||
|
|
||||||
|
expect($checks['imagick']['status'])->toBe($status)
|
||||||
|
->and($checks['imagick']['detail'])->toContain('php8.4-imagick');
|
||||||
|
})->with([
|
||||||
|
'precheck off' => [false, 'warn'],
|
||||||
|
'precheck on' => [true, 'critical'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
test('snapshot reports imagick as ok when it is loaded', function () {
|
||||||
|
$checks = collect(app(AdminSystemHealth::class)->snapshot()['checks'])->keyBy('key');
|
||||||
|
|
||||||
|
expect($checks['imagick']['status'])->toBe(class_exists(Imagick::class) ? 'ok' : 'warn');
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,10 @@
|
||||||
|
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Support\Monitoring\ErrorReportScrubber;
|
use App\Support\Monitoring\ErrorReportScrubber;
|
||||||
|
use Illuminate\Contracts\Debug\ExceptionHandler;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
use Livewire\Features\SupportFileUploads\MissingFileUploadsTraitException;
|
||||||
|
use Livewire\Features\SupportLockedProperties\CannotUpdateLockedPropertyException;
|
||||||
use Sentry\Event;
|
use Sentry\Event;
|
||||||
use Sentry\ExceptionDataBag;
|
use Sentry\ExceptionDataBag;
|
||||||
use Sentry\SentrySdk;
|
use Sentry\SentrySdk;
|
||||||
|
|
@ -44,6 +47,27 @@ test('a mistyped artisan command is not reported', function () {
|
||||||
expect($glitchTip->events)->toBe([]);
|
expect($glitchTip->events)->toBe([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('tampered livewire requests are answered with 400 and not reported', function (Closure $makeException) {
|
||||||
|
$glitchTip = FakeGlitchTip::install();
|
||||||
|
$exception = $makeException();
|
||||||
|
|
||||||
|
report($exception);
|
||||||
|
|
||||||
|
$response = app(ExceptionHandler::class)->render(request(), $exception);
|
||||||
|
|
||||||
|
expect($glitchTip->events)->toBe([])
|
||||||
|
->and($response->getStatusCode())->toBe(400);
|
||||||
|
})->with([
|
||||||
|
'locked property' => fn () => new CannotUpdateLockedPropertyException('portal'),
|
||||||
|
'upload without trait' => fn () => new MissingFileUploadsTraitException(new class
|
||||||
|
{
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return 'web.press-release-feed';
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
test('request data loses tokens, signatures and bodies', function () {
|
test('request data loses tokens, signatures and bodies', function () {
|
||||||
$scrubbed = ErrorReportScrubber::scrubRequest([
|
$scrubbed = ErrorReportScrubber::scrubRequest([
|
||||||
'url' => 'https://pressekonto.test/newsroom/claim/5?signature=abc123&expires=1700000000&src=mail_publish',
|
'url' => 'https://pressekonto.test/newsroom/claim/5?signature=abc123&expires=1700000000&src=mail_publish',
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue