diff --git a/middleware.go b/middleware.go index f215187..32e5a94 100644 --- a/middleware.go +++ b/middleware.go @@ -110,12 +110,11 @@ func buildEngineConfig(cfg GuardConfig) (*guardcore.SecurityConfig, error) { // accumulates every client's violations, so auto-banning by default // would let a handful of flagged requests lock out all users behind // a load balancer. - // NOTE: the engine exposes EnableRateLimitAutoBan, but its - // route-aware CheckRateLimit path (the one every adapter uses) - // never feeds the autoban counter, so the knob is inert through - // any middleware. It stays unmapped here until the engine wires - // it; advertising it would be a silent no-op. + // Rate-limit violations feed the same auto-ban counter as payload + // violations once the engine wires EnableRateLimitAutoBan through + // its route-aware rate limit check (guard-core-go after v4.3.2). c.EnableIPBanning = cfg.AutoBan && !cfg.Passive + c.EnableRateLimitAutoBan = cfg.AutoBan && !cfg.Passive if cfg.AutoBan { c.AutoBanThreshold = cfg.AutoBanThreshold c.AutoBanDuration = cfg.AutoBanDuration diff --git a/middleware_test.go b/middleware_test.go index c3a794f..94ddf36 100644 --- a/middleware_test.go +++ b/middleware_test.go @@ -368,3 +368,25 @@ func TestDeferredHandlerSwapsLiveGuardIn(t *testing.T) { } t.Fatal("deferred handler never swapped the live guard in") } + +func TestBuildEngineConfigMapsRateLimitAutoBan(t *testing.T) { + for _, tc := range []struct { + name string + autoBan, passive bool + want bool + }{ + {"auto ban on", true, false, true}, + {"auto ban off", false, false, false}, + {"passive never bans", true, true, false}, + } { + cfg := baseEnabled(nil) + cfg.AutoBan, cfg.Passive = tc.autoBan, tc.passive + got, err := buildEngineConfig(cfg) + if err != nil { + t.Fatalf("%s: %v", tc.name, err) + } + if got.EnableRateLimitAutoBan != tc.want { + t.Fatalf("%s: EnableRateLimitAutoBan = %v, want %v", tc.name, got.EnableRateLimitAutoBan, tc.want) + } + } +}