anomaly/throttle_security_check-extension
A lightweight extension for the Anomaly Throttle package that adds security-check integration to request throttling, helping Laravel apps flag or block suspicious traffic within existing throttle workflows.
This is an Anomaly Labs / PyroCMS (Streams Platform) extension, not a standalone Laravel package. Before you composer require it, confirm your app is actually in that ecosystem — if you're building a vanilla Laravel app, the package will pull in anomaly/* dependencies you don't want and you should read the "Gotchas" section instead.
Minimal steps:
composer require anomaly/throttle_security_check-extension
composer.json constraints first. Two stars and zero dependents means nobody is maintaining compatibility for you. Look at the require block and verify the Laravel/Illuminate version range matches your app before committing to the install.publishes() call exists, then diff the published file against the package default so you know the real knob names before you guess at env vars.First use case: protecting the control panel / admin login from credential stuffing. Install it, hit your login route with a wrong password 5–10 times in a row, and confirm you get blocked. That single test tells you whether the extension is wired correctly and whether the threshold defaults are sane for you.
Provider registration. Older Anomaly packages often predate Laravel's package auto-discovery. If nothing happens after install, add the provider to config/app.php manually — and register it after the Auth and Throttle providers, since it depends on their bindings being present.
Configuration over code. Whatever thresholds the package exposes (attempt count, decay window, lockout duration), drive them from config/ or env rather than editing the package. You want staging lenient and production strict without a code fork.
Layer it with, not instead of, Laravel's primitives. The idiomatic Laravel equivalent of this package is small enough to keep in your head:
// AppServiceProvider::boot()
RateLimiter::for('login', function (Request $request) {
$key = Str::lower($request->input('email')).'|'.$request->ip();
return [
Limit::perMinute(5)->by($key),
Limit::perMinute(20)->by($request->ip()),
];
});
Apply it with ->middleware('throttle:login') on the login POST route. If the extension already does this, don't add the middleware on top — see "double-throttling" below.
Pair IP and identity keys. IP-only throttling punishes everyone behind a shared NAT; email-only lets an attacker spray a million addresses from one host. The pattern that works is two counters — one keyed on email|ip, one keyed on ip alone — with the second set much higher.
Clear on success. Whatever increments the counter must be reset on Illuminate\Auth\Events\Login, otherwise a user who fat-fingers four times and then succeeds carries the debt into their session.
Notify on lockout. A silent lockout is an incident you find out about from a support ticket. Fire a log entry (or Slack ping) on the lockout event with the IP and attempted identifier — that's your early signal for a live attack.
Test it like a feature, not a unit. A Pest/PHPUnit test that loops $this->post('/login', [...]) with bad credentials and asserts a 429 on the Nth call is worth more than any amount of reading the source.
src/ before you ship it — it's probably under a few hundred lines — and be prepared to fork it into app/ if the upstream goes quiet.array driver (the default in phpunit.xml and often in local .env) means counters evaporate between requests and your tests will lie to you. Use Redis or the database store in staging, and assert against the same store you use in production.$request->ip() is the balancer unless you've configured TrustProxies. Get this wrong once and you'll lock out your entire user base simultaneously.throttle: middleware to the same route compounds the counters and produces lockouts at a fraction of the configured threshold. Pick one owner for the login limiter.anomaly/* siblings load in a specific order. If you get "class not found" or a null binding at boot, it's usually ordering, not a missing install.redis-cli --scan --pattern '*limiter*', or Cache::get() in tinker) rather than guessing. If the key never appears, the request never reached the limiter — check middleware assignment before you check the package.php artisan cache:clear between attempts, or you'll spend an hour chasing a "bug" that's a leftover counter from your last test run.RateLimiter shown above. You get the same protection, first-party support, and no supply-chain question. Reach for this extension only if you're already inside the Anomaly/Streams ecosystem and want its admin-side wiring for free.How can I help you explore Laravel packages today?