package servewaf // The heuristic abuseipdb. It reads SWWAF_ABUSEIPDB_KEY, // SWWAF_ABUSEIPDB_MIN_SCORE and SWWAF_REPUTATION_ACTION. import ( "context" "time" "sneak.berlin/go/smallwebwaf/internal/bans" "sneak.berlin/go/smallwebwaf/internal/ratelimit" "sneak.berlin/go/smallwebwaf/internal/reputation" "sneak.berlin/go/smallwebwaf/internal/requestlog" ) // abuseIPDB notes AbuseIPDB, as noteHit does, with the score, when its score // of the client is a hit, and refuses the request when SWWAF_REPUTATION_ACTION // is deny, as dnsbl does for a zone. While SWWAF_ABUSEIPDB_KEY is unset it does // nothing. A client without a score is checked in the background, by the // request's address, if its history counts an offence, and the request does // not wait for the answer. The score is then used for each address of the // client. Its figures are the score and SWWAF_ABUSEIPDB_MIN_SCORE. type abuseIPDB struct{} func (abuseIPDB) name() string { return "abuseipdb" } func (abuseIPDB) check(ctx context.Context, rq *request, _ time.Time) *verdict { if rq.h.config.AbuseIPDBKey == "" { return nil } client := rq.h.clientGroup(rq.client) held, _ := rq.h.limiter.Client(client) offender := held.History.Offences != ratelimit.Offences{} score, hit := rq.h.abuseIPDB.Hit(ctx, client, rq.client, offender) if !hit { return nil } rq.abuseIPDBHit = true rq.noteHit(bans.ReputationHit{Source: reputation.AbuseIPDBSource, Score: &score}, "scored by AbuseIPDB at or over SWWAF_ABUSEIPDB_MIN_SCORE") if rq.h.config.ReputationAction != deny { return nil } return &verdict{ action: requestlog.ActionDenied, figures: map[string]any{ figureScore: score, "min_score": rq.h.config.AbuseIPDBMinScore, }, } }