55 "context"
66 "fmt"
77 "io"
8+ "regexp"
89
910 "net/http"
1011 "net/netip"
@@ -183,26 +184,35 @@ func (b *BotBlocker) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
183184 log .Debugf ("Checking request: CIDR: \" %v\" user agent: \" %s\" " , req .RemoteAddr , req .UserAgent ())
184185 // Using an external plugin to avoid https://github.com/traefik/yaegi/issues/1697
185186 timer := getTimer (startTime )
186- defer timer ()
187187
188188 remoteAddrPort , err := netip .ParseAddrPort (req .RemoteAddr )
189189 if err != nil {
190+ timer ()
190191 http .Error (rw , "internal error" , http .StatusInternalServerError )
191192 return
192193 }
193194 if b .shouldBlockIp (remoteAddrPort .Addr ()) {
194- log .Infof ("blocked request with from IP %v" , remoteAddrPort .Addr ())
195+ log .Infof ("blocked request with from IP \" %v\" " , remoteAddrPort .Addr ())
196+ timer ()
195197 http .Error (rw , "blocked" , http .StatusForbidden )
196198 return
197199 }
198200
199201 agent := strings .ToLower (req .UserAgent ())
200- if b .shouldBlockAgent (agent ) {
201- log .Infof ("blocked request with user agent %v because it contained %v" , agent , agent )
202+ blocked , badAgent , err := b .shouldBlockAgent (agent )
203+ if err != nil {
204+ timer ()
205+ http .Error (rw , "internal error" , http .StatusInternalServerError )
206+ return
207+ }
208+ if blocked {
209+ log .Infof ("blocked request with user agent \" %v\" because it contained \" %v\" " , agent , badAgent )
210+ timer ()
202211 http .Error (rw , "blocked" , http .StatusForbidden )
203212 return
204213 }
205214
215+ timer ()
206216 b .next .ServeHTTP (rw , req )
207217}
208218
@@ -215,14 +225,23 @@ func (b *BotBlocker) shouldBlockIp(addr netip.Addr) bool {
215225 return false
216226}
217227
218- func (b * BotBlocker ) shouldBlockAgent (userAgent string ) bool {
228+ func (b * BotBlocker ) shouldBlockAgent (userAgent string ) ( bool , string , error ) {
219229 userAgent = strings .ToLower (strings .TrimSpace (userAgent ))
220230 for _ , badAgent := range b .userAgentBlockList {
231+ // fast check with contains
221232 if strings .Contains (userAgent , badAgent ) {
222- return true
233+ // verify with regex
234+ pattern := fmt .Sprintf (`(?:\b)%s(?:\b)` , badAgent )
235+ matched , err := regexp .Match (pattern , []byte (userAgent ))
236+ if err != nil {
237+ return false , "" , fmt .Errorf ("failed to check user agent %s: %e" , userAgent , err )
238+ }
239+ if matched {
240+ return true , badAgent , nil
241+ }
223242 }
224243 }
225- return false
244+ return false , "" , nil
226245}
227246
228247func getTimer (startTime time.Time ) func () {
0 commit comments