call($method, $uri, $data, [], [], ['REMOTE_ADDR' => $ip]); } public function test_wp_login_decoy_serves_fake_wordpress_and_bans(): void { $response = $this->fromIp('GET', '/wp-login.php', self::ATTACKER_IP); $response->assertStatus(200); $response->assertSee('wp-submit', false); $response->assertSee('WordPress', false); $this->assertTrue(BannedIp::where('ip', self::ATTACKER_IP)->exists()); $this->assertTrue(AuditEvent::where('action', 'security.honeypot_hit')->exists()); } public function test_env_decoy_leaks_canary_and_bans(): void { $canary = config('clusev.honeypot.canaries.app_key'); $response = $this->fromIp('GET', '/.env', self::ATTACKER_IP); $response->assertStatus(200); $response->assertSee($canary, false); $this->assertTrue(BannedIp::where('ip', self::ATTACKER_IP)->exists()); $this->assertTrue(AuditEvent::where('action', 'security.honeypot_hit')->exists()); } public function test_honeytoken_replay_is_blocked_banned_and_audited(): void { $canary = config('clusev.honeypot.canaries.db_password'); // A normal POST web route carrying the leaked canary in a form field trips the middleware // (broadcasting/auth is in the web group, so the appended DetectHoneytoken runs). The 403 // fires before the route's own handler, proving the tripwire, not the endpoint, responded. $response = $this->fromIp('POST', '/broadcasting/auth', self::ATTACKER_IP, ['password' => $canary]); $response->assertStatus(403); $this->assertTrue(BannedIp::where('ip', self::ATTACKER_IP)->exists()); $this->assertTrue( AuditEvent::where('action', 'security.honeytoken_used') ->where('target', 'db_password') ->where('ip', self::ATTACKER_IP) ->exists() ); } public function test_disabled_honeypot_returns_404_without_ban_or_audit(): void { config(['clusev.honeypot.enabled' => false]); $response = $this->fromIp('GET', '/wp-login.php', self::ATTACKER_IP); $response->assertStatus(404); $this->assertDatabaseCount('banned_ips', 0); $this->assertFalse(AuditEvent::where('action', 'security.honeypot_hit')->exists()); } public function test_exempt_ip_gets_deception_but_is_not_banned(): void { // Default test client IP is 127.0.0.1 (loopback → exempt): deception still fires, ban does not. $response = $this->get('/wp-login.php'); $response->assertStatus(200); $response->assertSee('wp-submit', false); // The hit is still audited, but banNow no-ops for the exempt IP → no ban row. $this->assertTrue(AuditEvent::where('action', 'security.honeypot_hit')->exists()); $this->assertDatabaseCount('banned_ips', 0); } }