debianProfile(); $detector = Mockery::mock(OsDetector::class); $detector->shouldReceive('detect')->andReturn($os); $captured = ''; $fleet = Mockery::mock(FleetService::class); $fleet->shouldReceive('runPrivileged') ->andReturnUsing(function ($server, $cmd) use (&$captured) { $captured = $cmd; // fail2ban running from a source/`rc` install: present + active. return ['ok' => true, 'output' => "fail2ban 1 active\nfirewall 1 active\nautoupdate 0 inactive"]; }); $service = new HardeningService($fleet, Mockery::mock(FirewallService::class), $detector); $rows = $service->state(new Server); // fail2ban installed-probe gains the binary fallback (was dpkg ^ii only — the bug). $this->assertStringContainsString('command -v fail2ban-client', $captured); // firewall installed-probe gains the package-manager fallback (was command -v only). $this->assertStringContainsString('dpkg -l ufw', $captured); $f2b = collect($rows)->firstWhere('key', 'fail2ban'); $this->assertTrue($f2b['featureOn'], 'fail2ban active must read as on'); } public function test_autoupdate_row_is_neutral_and_never_insecure(): void { $os = $this->debianProfile(); $detector = Mockery::mock(OsDetector::class); $detector->shouldReceive('detect')->andReturn($os); $fleet = Mockery::mock(FleetService::class); $fleet->shouldReceive('runPrivileged')->andReturn([ 'ok' => true, // auto-update reported INACTIVE by the host: 'output' => "fail2ban 1 active\nfirewall 1 active\nautoupdate 1 inactive", ]); $service = new HardeningService($fleet, Mockery::mock(FirewallService::class), $detector); $rows = $service->state(new Server); $au = collect($rows)->firstWhere('key', 'unattended'); $this->assertTrue($au['secure'], 'auto-updates must never read as insecure'); $this->assertTrue($au['neutral'] ?? false, 'auto-updates row must be flagged neutral'); $this->assertFalse($au['featureOn'], 'inactive auto-updates → toggle shows enable'); } }