From 8f630c5093407cefbb8ed7c06b24a430da8dc7bd Mon Sep 17 00:00:00 2001 From: nexxo Date: Mon, 3 Aug 2026 13:03:35 +0200 Subject: [PATCH] Fix-Runde 1: strukturelle Pruefung statt Zaehler, und der nicht erreichbare Host bekommt einen Test substr_count('flags timeout') lief ueber den ganzen Text inklusive Kommentare und belegte nur "die Phrase kommt zweimal vor", nicht "beide set-Bloecke tragen die Ablaufzeit". Ersetzt durch je einen strukturellen Ausdruck pro Menge; der Originalkommentar aus dem Auftragszettel kann damit wieder wortgenau stehen. Dazu zwei neue Tests mit failConnect, die belegen, dass block()/release() bei einem nicht erreichbaren Host false liefern statt zu werfen - der Pfad, auf dem das Wiedereintragen in Aufgabe 4 aufbaut. --- .../Steps/Host/SecureHostFirewall.php | 7 ++-- tests/Feature/Security/HostFirewallTest.php | 37 +++++++++++++++++-- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/app/Provisioning/Steps/Host/SecureHostFirewall.php b/app/Provisioning/Steps/Host/SecureHostFirewall.php index 705b13d..cd4de17 100644 --- a/app/Provisioning/Steps/Host/SecureHostFirewall.php +++ b/app/Provisioning/Steps/Host/SecureHostFirewall.php @@ -123,10 +123,9 @@ class SecureHostFirewall extends HostStep flush ruleset table inet clupilot_filter { - # Adressen, die gerade gesperrt sind. Die Option `timeout` in den `flags` - # ist nicht schmückend: ohne sie nimmt nftables beim Eintragen gar keine - # Zeitangabe an — und die Frist liefe dann nur in unserer Datenbank ab, - # nicht im Kernel. + # Adressen, die gerade gesperrt sind. `flags timeout` ist nicht schmückend: + # ohne das nimmt nftables beim Eintragen gar keine Zeitangabe an — und die + # Frist liefe dann nur in unserer Datenbank ab, nicht im Kernel. set clupilot_blocked { type ipv4_addr flags timeout diff --git a/tests/Feature/Security/HostFirewallTest.php b/tests/Feature/Security/HostFirewallTest.php index fc057cf..810515b 100644 --- a/tests/Feature/Security/HostFirewallTest.php +++ b/tests/Feature/Security/HostFirewallTest.php @@ -27,10 +27,14 @@ it('stellt die Sperrregel UNTER die Regel fuer bestehende Verbindungen', functio ->and($established)->toBeLessThan($sperre); // Beide Mengen, und beide mit Ablaufzeit — ohne `flags timeout` nimmt - // nftables die Zeitangabe beim Eintragen gar nicht an. - expect($regelwerk)->toContain('set clupilot_blocked') - ->and($regelwerk)->toContain('set clupilot_blocked6') - ->and(substr_count($regelwerk, 'flags timeout'))->toBe(2); + // nftables die Zeitangabe beim Eintragen gar nicht an. Strukturell geprüft, + // je Menge einzeln: ein bloßer substr_count('flags timeout') über den + // GANZEN Text (Kommentare eingeschlossen) belegt nur „die Phrase kommt + // zweimal vor", nicht „beide set-Blöcke tragen die Ablaufzeit" — er bliebe + // grün, wenn clupilot_blocked6 die Zeitoption verlöre, während dieselbe + // Phrase zufällig zweimal in Prosa stünde. + expect($regelwerk)->toMatch('/set clupilot_blocked \{\s*type ipv4_addr\s*flags timeout\s*\}/') + ->and($regelwerk)->toMatch('/set clupilot_blocked6 \{\s*type ipv6_addr\s*flags timeout\s*\}/'); }); it('traegt eine Adresse mit Ablaufzeit ein und nimmt sie wieder heraus', function () { @@ -54,3 +58,28 @@ it('waehlt fuer eine IPv6-Adresse die zweite Menge', function () { expect($shell->ran('clupilot_blocked6 { 2001:db8::1 timeout 3600s }'))->toBeTrue(); }); + +it('gibt false zurueck statt zu werfen, wenn der Host beim Sperren nicht erreichbar ist', function () { + // Trägt Aufgabe 4: der Sperrdatensatz entsteht auch dann in der Datenbank + // und wird beim nächsten Lauf erneut eingetragen. Eine Ausnahme hier würde + // stattdessen den ganzen Zeitplan-Auftrag mitreißen, der block() aufruft. + $shell = new FakeRemoteShell; + $shell->failConnect = true; + app()->instance(\App\Services\Ssh\RemoteShell::class, $shell); + $host = Host::factory()->active()->create(['ssh_host_key' => 'SHA256:abc']); + + $result = app(HostFirewall::class)->block($host, '203.0.113.7', 3600); + + expect($result)->toBeFalse(); +}); + +it('gibt false zurueck statt zu werfen, wenn der Host beim Entsperren nicht erreichbar ist', function () { + $shell = new FakeRemoteShell; + $shell->failConnect = true; + app()->instance(\App\Services\Ssh\RemoteShell::class, $shell); + $host = Host::factory()->active()->create(['ssh_host_key' => 'SHA256:abc']); + + $result = app(HostFirewall::class)->release($host, '203.0.113.7'); + + expect($result)->toBeFalse(); +});