Quelltext des aktuellen Standes
<?php
declare(strict_types=1);
use OpenNIT\Controllers\Admin\MonitoringAdminController;
/**
* Deckt die Zabbix-Vorlage ab, die sich unter *Alerts → Media types → Import*
* einspielen lässt.
*
* Eine Vorlage, die Zabbix beim Import abweist, ist schlimmer als keine – der
* Aufbau wird deshalb hier festgehalten, samt der Merkmale, an denen sich ein
* verrutschtes YAML zeigt.
*/
return static function (): void {
/** Erzeugt die Vorlage ohne App-Bootstrap. */
$yaml = static function (
string $url = 'https://doku.example.test/monitoring/webhook/zabbix',
string $token = 'abc123'
): string {
$c = new \ReflectionClass(MonitoringAdminController::class);
$m = $c->getMethod('zabbixTemplateYaml');
$m->setAccessible(true);
return (string)$m->invoke($c->newInstanceWithoutConstructor(), $url, $token);
};
test('Der Kopf nennt eine Fassung, die jeder neuere Stand einspielt', function () use ($yaml) {
$text = $yaml();
assertTrue(str_contains($text, 'zabbix_export:'), 'Der Wurzelknoten fehlt');
// Zabbix spielt eine ältere Fassung in einen neueren Stand ein,
// umgekehrt nicht – deshalb die niedrigste noch gepflegte.
assertTrue(str_contains($text, " version: '6.0'"), 'Die Fassung fehlt oder ist zu hoch');
assertTrue(str_contains($text, ' media_types:'), 'Der Abschnitt der Media-Types fehlt');
assertTrue(str_contains($text, ' type: WEBHOOK'), 'Der Typ fehlt');
});
test('Adresse und Schlüssel stehen bereits darin', function () use ($yaml) {
$text = $yaml('https://doku.example.test/monitoring/webhook/zabbix', 'deadbeef');
assertTrue(str_contains($text, " value: 'https://doku.example.test/monitoring/webhook/zabbix'"));
assertTrue(str_contains($text, " value: 'deadbeef'"));
});
test('Jede Zabbix-Marke steht in Hochkommata', function () use ($yaml) {
// Eine geschweifte Klammer am Anfang eines Wertes wäre für YAML der
// Beginn einer Zuordnung – der Import bräche ab.
foreach (explode("\n", $yaml()) as $zeile) {
if (!str_contains($zeile, '{')) {
continue;
}
if (!str_starts_with(ltrim($zeile), 'value:')) {
continue; // Skript und Beschreibung stehen als Block
}
assertTrue(
(bool)preg_match("/^\s*value: '.*'$/", $zeile),
'Ungeschützter Wert: ' . trim($zeile)
);
}
});
test('Ein Hochkomma im Schlüssel bricht die Vorlage nicht', function () use ($yaml) {
// YAML verdoppelt es; sonst endete die Zeichenkette mitten im Wert.
assertTrue(str_contains($yaml('https://x.test/w', "a'b"), "value: 'a''b'"));
});
test('Die Vorlage führt die Parameter, die der Endpunkt erwartet', function () use ($yaml) {
$text = $yaml();
foreach ([
'event_id', 'host', 'hostname', 'message', 'severity',
'status', 'trigger_name', 'url', 'webhook_token', 'webhook_url',
] as $name) {
assertTrue(str_contains($text, " - name: '" . $name . "'"), 'Parameter fehlt: ' . $name);
}
// Sprachunabhängig, wie in der Anleitung empfohlen.
assertTrue(str_contains($text, " value: '{EVENT.VALUE}'"));
});
test('Das Skript beginnt ohne zusätzliche Einrückung im Block', function () use ($yaml) {
$text = $yaml();
$anfang = strpos($text, ' script: |');
assertTrue($anfang !== false, 'Der Skript-Block fehlt');
$erste = explode("\n", substr($text, $anfang))[1];
// Acht Zeichen Einrückung: der Block liegt zwei Stufen unter `script`.
assertSame(' var p = JSON.parse(value);', $erste);
});
test('Die Vorlage bringt Nachrichtenvorlagen mit', function () use ($yaml) {
// Ohne sie weist Zabbix eine Operation mit „Default message" ab – die
// Action liesse sich dann nicht speichern.
$text = $yaml();
assertTrue(str_contains($text, ' message_templates:'), 'Der Abschnitt fehlt');
foreach (['PROBLEM', 'RECOVERY', 'UPDATE'] as $modus) {
assertTrue(
str_contains($text, ' operation_mode: ' . $modus),
'Nachrichtenvorlage fehlt: ' . $modus
);
}
assertSame(3, substr_count($text, ' - event_source: TRIGGERS'));
});
test('Ohne Schlüssel gibt es keine Vorlage', function () {
// Eine Vorlage mit leerem Feld sähe eingerichtet aus, ohne es zu sein.
$src = (string)file_get_contents(
dirname(__DIR__) . '/src/Controllers/Admin/MonitoringAdminController.php'
);
$start = strpos($src, 'public function zabbixTemplate(');
assertTrue($start !== false, 'Der Download fehlt');
$rumpf = substr($src, $start, 1200);
assertTrue(str_contains($rumpf, "if (\$token === '')"), 'Der leere Schlüssel wird nicht abgefangen');
assertTrue(str_contains($rumpf, '$this->authorize(\'admin_monitoring\')'), 'Der Download ist nicht geschützt');
assertTrue(str_contains($rumpf, 'Content-Disposition'), 'Die Datei wird nicht als Download ausgeliefert');
// Der Schlüssel verlässt die Anwendung – das gehört ins Protokoll,
// der Schlüssel selbst aber nicht.
assertTrue(str_contains($rumpf, '$this->audit()->log('), 'Der Download steht nicht im Protokoll');
assertFalse(str_contains($rumpf, "'token' => \$token"), 'Der Schlüssel darf nicht ins Protokoll');
});
test('Der Download hängt an einer Route', function () {
$src = (string)file_get_contents(dirname(__DIR__) . '/src/Core/Application.php');
assertTrue(
str_contains($src, "'/admin/monitoring/zabbix-template'"),
'Die Route der Vorlage fehlt'
);
});
};