Shield Corev1.2.x
Testing & CI
Shield Core punya test suite sendiri. Halaman ini membahas apa yang perlu Anda uji di package adapter Anda, dan alat-alat yang repository ini menyediakan.
Perkakas di repository Core
Section titled “Perkakas di repository Core”composer test # Pestcomposer analyse # PHPStan level 8composer format-test # Pint, mode periksacomposer mutate # mutation testingTiga perkakas lain di luar Composer:
php tools/scanner-simulator/simulate.php # pemutaran ulang corpus scannerphp tools/scanner-simulator/benchmark.php # latensi jalur allowphp tools/replay.php # deteksi dari log kejadian nyatabenchmark.php paling berguna untuk Anda
Section titled “benchmark.php paling berguna untuk Anda”Shield dipanggil di setiap request. Kalau adapter Anda menambah 5 milidetik, itu 5 milidetik di setiap halaman.
php tools/scanner-simulator/benchmark.phpTargetnya jalur allow di bawah 2 milidetik. Jalankan setelah setiap perubahan yang menyentuh kode request.
replay.php untuk verifikasi yang lebih jujur
Section titled “replay.php untuk verifikasi yang lebih jujur”Mengambil log kejadian nyata dan menjalankan ulang request tersebut lewat engine. Ini cara paling bagus untuk mengukur apakah perubahan Anda memperbaiki atau merusak keputusan lama.
Mengatur waktu
Section titled “Mengatur waktu”Semua waktu di Shield datang dari ClockInterface. Ini satu-satunya cara
menguji pelusan dan kedaluwarsa tanpa menunggu.
use Ganadev\Shield\Core\Clock\ClockInterface;
final class FrozenClock implements ClockInterface{ public function __construct( private \DateTimeImmutable $now = new \DateTimeImmutable('2026-01-01 00:00:00'), ) {}
public function now(): \DateTimeImmutable { return $this->now; }
public function advance(string $modifier): void { $this->now = $this->now->modify($modifier); }}Dengan begitu Anda bisa menguji pelusan offense tanpa tidur 24 jam:
$clock = new FrozenClock;$engine = $this->engine($clock);
// Ban dengan offense_count 3$engine->inspect($context, $counters);
// Belum ada jeda — offense tetap 3expect($this->offenseCount())->toBe(3);
// Setelah 24 jam tanpa aktivitas$clock->advance('+25 hours');$engine->inspect($context, $counters);
// Sekarang 2expect($this->offenseCount())->toBe(2);Skenario wajib
Section titled “Skenario wajib”Lima skenario ini menutup bagian yang paling sering rusak:
1. Normalisasi
Section titled “1. Normalisasi”test('encoding berlapis diselesaikan', function () { $result = $this->inspect('/%2e%65nv');
expect($result->shouldBlock())->toBeTrue();});Satu kasus ini menutup banyak payload.
2. Allowlist tidak menutupi critical
Section titled “2. Allowlist tidak menutupi critical”test('allowlist tidak menutupi rule critical', function () { config(['shield.allowlist.paths' => ['/']]);
$result = $this->inspect('/.env');
expect($result->shouldBlock())->toBeTrue();});Ini regresi yang mahal kalau sampai lolos.
3. observe menurunkan, tidak menaikkan
Section titled “3. observe menurunkan, tidak menaikkan”test('mode observe tidak pernah memblokir', function () { config(['shield.mode' => 'observe']);
$result = $this->inspect('/.env');
expect($result->shouldBlock())->toBeFalse(); expect($result->verdict->decision)->toBe(Decision::Observe); expect($result->verdict->intended)->toBe(Decision::BlockRequest); expect($result->verdict->reason) ->toStartWith('observe_mode:');});Perhatikan dua lapis yang dibandingkan berbeda. Kalau test Anda hanya
memeriksa shouldBlock(), ia tidak akan menangkap mode yang keliru.
4. Kegagalan infrastruktur
Section titled “4. Kegagalan infrastruktur”test('repository gagal tidak menjatuhkan request', function () { $this->bans->shouldReceive('findActiveByIp') ->andThrow(new RuntimeException('database down'));
$result = $this->inspect('/');
expect($result->infrastructureDegraded)->toBeTrue(); expect($result->shouldBlock())->toBeFalse();});5. Body tidak pernah bocor
Section titled “5. Body tidak pernah bocor”test('isi body tidak masuk log', function () { $this->inspect('/api', 'POST', body: 'password=rahasia123');
expect($this->recordedEvents())->not->toContain('rahasia123');});Ini yang membuat Shield layak dipakai di produksi.
Skenario tambahan untuk adapter
Section titled “Skenario tambahan untuk adapter”| Skenario | Yang diuji |
|---|---|
| Cookie tepercaya valid | Tidak kena challenge lagi. |
| Cookie tepercaya dari domain lain | Ditolak. |
fail_mode = closed dengan DB mati |
Request diblokir dengan alasan fail_closed. |
Path di skip_paths |
Perilaku nol, signature tetap jalan. |
mode = challenge dengan critical |
Tetap BLOCK_REQUEST. |
| IP sudah di-ban lalu lolos challenge | Ban dilepas, challengePassedAt terisi. |
| Melepas ban lalu offense lagi | offenseCount naik, bukan reset. |
| Bot terverifikasi | Skor perilaku nol. |
| Klaim bot gagal diverifikasi | unverified_crawler_claim muncul. |
URI melewati max_uri_length |
Dipotong, bukan error. |
Yang perlu dinilai di CI
Section titled “Yang perlu dinilai di CI”composer analyse # wajib. PHPStan level 8 menangkap kesalahan tipe kontrak.composer test # wajib.composer format-testMutation testing (composer mutate) butuh xdebug atau pcov, jadi biasanya
dijalankan terpisah, tidak di setiap commit.
Untuk adapter, tambahkan:
php tools/scanner-simulator/benchmark.php # harus di bawah 2 msKalau latensi naik, commit-nya belum selesai.
Menguji dalam dua mode
Section titled “Menguji dalam dua mode”Jalankan test yang sama dengan dua mode berbeda:
it('tidak pernah memblokir dalam observe mode', function () { config(['shield.mode' => 'observe']); // semua kasus harus ALLOW atau OBSERVE})->with(['observe', 'challenge', 'enforce']);Kalau ada kasus yang hanya lulus di satu mode, ada bug di mode yang lain. Perbedaan mode seharusnya hanya soal respons, bukan soal data.
Pesan kegagalan yang berguna
Section titled “Pesan kegagalan yang berguna”Kalau sebuah test gagal, verdict->reason biasanya langsung menjelaskan
kenapa:
| Alasan | Artinya |
|---|---|
critical_signature_sensitive.env |
Rule cocok. Bukan bug scoring. |
score_ban |
Skor melewati ambang. Periksa score->total. |
active_ban_challenge |
Ban aktif menaikkan jadi challenge. |
trusted_cookie |
Cookie yang membuat request lolos. |
observe_mode: would_have_been_... |
Mode observe menahan sesuatu. |
fail_closed |
Infrastruktur bermasalah. |
fail_closed satu-satunya yang biasanya berarti ada masalah lingkungan,
bukan logika.
Yang tidak perlu diuji
Section titled “Yang tidak perlu diuji”- Perilaku default framework — itu tugas framework.
- Tampilan HTML challenge — itu presentasi.
- Driver pihak ketiga — provider itu yang menguji dirinya sendiri.
Yang perlu diuji package Anda: mapping dari dunia framework Anda ke dunia Shield, dan mapping kembali ke respons. Titik bifurasi itulah tempat bug tinggal.
Powered by PT Ganadev Multi Solusi