Files
2026-11-11 23:19:20 +01:00

101 lines
3.4 KiB
PHP

<?php
use Carbon\CarbonImmutable;
use Illuminate\Support\Facades\Route;
use Mattitja\BadIpBlocker\Facades\BadIpBlocker;
it('lets normal visitors through', function () {
$this->getFrom('/', '203.0.113.10')->assertOk()->assertSee('ok');
});
it('blocks ips on the list', function () {
BadIpBlocker::block('203.0.113.10');
$this->getFrom('/', '203.0.113.10')->assertStatus(418)->assertSee('Blocked.');
$this->getFrom('/', '192.0.2.30')->assertOk();
});
it('uses the configured response', function () {
config(['bad-ip-blocker.response' => ['status' => 403, 'message' => 'Nope']]);
BadIpBlocker::block('203.0.113.10');
$this->getFrom('/', '203.0.113.10')->assertForbidden()->assertSee('Nope');
});
it('does nothing when disabled', function () {
config(['bad-ip-blocker.enabled' => false]);
BadIpBlocker::block('203.0.113.10');
$this->getFrom('/', '203.0.113.10')->assertOk();
});
it('never blocks whitelisted ranges, including private proxy addresses', function () {
BadIpBlocker::block('192.168.1.10');
$this->getFrom('/', '192.168.1.10')->assertOk();
$this->getFrom('/.well-known/security.txt', '192.168.1.20')->assertNotFound();
expect(BadIpBlocker::isBlocked('192.168.1.20'))->toBeFalse();
});
it('auto-blocks clients probing for a missing security.txt', function (string $path) {
$this->getFrom($path, '198.51.100.20')->assertStatus(418);
expect(BadIpBlocker::isBlocked('198.51.100.20'))->toBeTrue()
->and(BadIpBlocker::all()['198.51.100.20']['reason'])->toBe('Auto: '.$path);
$this->getFrom('/', '198.51.100.20')->assertStatus(418);
})->with(['/.well-known/security.txt', '/security.txt', '/.env', '/wp-login.php']);
it('does not auto-block when the path exists in the app', function () {
Route::get('/.well-known/security.txt', fn () => 'Contact: mailto:security@example.com');
$this->getFrom('/.well-known/security.txt', '198.51.100.20')->assertOk();
expect(BadIpBlocker::isBlocked('198.51.100.20'))->toBeFalse();
});
it('does not auto-block ordinary 404s', function () {
foreach (['/about', '/products', '/missing-page'] as $path) {
$this->getFrom($path, '203.0.113.10')->assertNotFound();
}
expect(BadIpBlocker::isBlocked('203.0.113.10'))->toBeFalse();
});
it('respects max_hits', function () {
config(['bad-ip-blocker.auto_block.max_hits' => 2]);
$this->getFrom('/.well-known/security.txt', '198.51.100.20')->assertNotFound();
expect(BadIpBlocker::isBlocked('198.51.100.20'))->toBeFalse();
$this->getFrom('/security.txt', '198.51.100.20')->assertStatus(418);
expect(BadIpBlocker::isBlocked('198.51.100.20'))->toBeTrue();
});
it('expires auto-blocks after 7 days by default', function () {
CarbonImmutable::setTestNow('2026-11-11 20:00:00');
$this->getFrom('/security.txt', '198.51.100.20');
expect(BadIpBlocker::all()['198.51.100.20']['expires_at'])->toBe('2026-11-18T20:00:00+00:00');
CarbonImmutable::setTestNow();
});
it('can make auto-blocks permanent', function () {
config(['bad-ip-blocker.auto_block.ttl' => null]);
$this->getFrom('/security.txt', '198.51.100.20');
expect(BadIpBlocker::all()['198.51.100.20']['expires_at'])->toBeNull();
});
it('can disable auto-blocking', function () {
config(['bad-ip-blocker.auto_block.enabled' => false]);
$this->getFrom('/security.txt', '198.51.100.20')->assertNotFound();
expect(BadIpBlocker::isBlocked('198.51.100.20'))->toBeFalse();
});