script/fmt now runs golines -m 77 after gofmt, repeating golines until it changes nothing, since one pass can leave a line the next splits. script/fmt-check fails when either would change a file. script/bootstrap installs golines v0.13.0 with go install, pinned to its commit, and checks the installed version with go version -m. The tree is reformatted. The longer lines this makes are fixed by hand: nolint directives moved onto their own line above the statement, one string split, and 20 functions over funlen's 80 lines shortened or split, RunWAF and limitPercentages among them. Model: opus-5-5
This commit is contained in:
1 parent
63e58e326b
commit
7f8a59de04
118 files changed
+9447
-2670
No files matched your search
+267
-91
@@ -46,7 +46,9 @@ const (
|
||||
asnURL = "https://lists.example/asn.txt"
|
||||
)
|
||||
|
||||
func TestEachBlocklistActionForAListedAddressAndAListedNetblock(t *testing.T) {
|
||||
func TestEachBlocklistActionForAListedAddressAndAListedNetblock(
|
||||
t *testing.T,
|
||||
) {
|
||||
t.Parallel()
|
||||
|
||||
forward, denied := requestlog.ActionForward, requestlog.ActionClientDenied
|
||||
@@ -90,14 +92,23 @@ func TestEachBlocklistActionForAListedAddressAndAListedNetblock(t *testing.T) {
|
||||
for i := range 3 {
|
||||
line := s.get(from, tc.statuses[i], tc.actions[i])
|
||||
wantReputation(t, line, dropURL)
|
||||
wantPercent(t, "rate_limit_percent", line.RateLimitPercent,
|
||||
line.RateLimitPercentSetting, tc.percent)
|
||||
wantPercent(
|
||||
t,
|
||||
"rate_limit_percent",
|
||||
line.RateLimitPercent,
|
||||
line.RateLimitPercentSetting,
|
||||
tc.percent,
|
||||
)
|
||||
|
||||
// A request refused for the list is not counted.
|
||||
counted := line.fields["counts"] != nil
|
||||
if counted != (tc.actions[i] != denied) {
|
||||
t.Errorf("request from %s counted %t, logged %s", from, counted,
|
||||
tc.actions[i])
|
||||
t.Errorf(
|
||||
"request from %s counted %t, logged %s",
|
||||
from,
|
||||
counted,
|
||||
tc.actions[i],
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,14 +123,17 @@ func TestEachBlocklistActionForAListedAddressAndAListedNetblock(t *testing.T) {
|
||||
}
|
||||
|
||||
// A refusal for the list makes no ban.
|
||||
if held := server.Ledger.Snapshot(); tc.action == actionDeny && len(held) != 0 {
|
||||
if held := server.Ledger.Snapshot(); tc.action == actionDeny &&
|
||||
len(held) != 0 {
|
||||
t.Errorf("bans %+v, want none", held)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReputationRefusalNamesItsHeuristicAndWhatListedTheClient(t *testing.T) {
|
||||
func TestReputationRefusalNamesItsHeuristicAndWhatListedTheClient(
|
||||
t *testing.T,
|
||||
) {
|
||||
t.Parallel()
|
||||
|
||||
for _, tc := range []struct {
|
||||
@@ -163,7 +177,11 @@ func TestReputationRefusalNamesItsHeuristicAndWhatListedTheClient(t *testing.T)
|
||||
s, server, _ := startWithLookups(t, tc.env)
|
||||
tc.load(t, server)
|
||||
|
||||
line := s.get(client, http.StatusForbidden, requestlog.ActionClientDenied)
|
||||
line := s.get(
|
||||
client,
|
||||
http.StatusForbidden,
|
||||
requestlog.ActionClientDenied,
|
||||
)
|
||||
wantHeuristic(t, line, tc.heuristic, tc.figures)
|
||||
})
|
||||
}
|
||||
@@ -180,12 +198,17 @@ func TestBlocklistsComeAfterAllowedCountriesAndSkipAllowNets(t *testing.T) {
|
||||
// fromKP's country refuses it before the list is looked at, and fromDE,
|
||||
// in SWW_ALLOW_NETS, is not checked at all: neither is noted, nor
|
||||
// alerted.
|
||||
wantReputation(t, s.get(fromKP, http.StatusForbidden, requestlog.ActionCountryDenied))
|
||||
wantReputation(
|
||||
t,
|
||||
s.get(fromKP, http.StatusForbidden, requestlog.ActionCountryDenied),
|
||||
)
|
||||
wantReputation(t, s.get(fromDE, http.StatusOK, requestlog.ActionForward))
|
||||
wantAlerts(t, queue)
|
||||
}
|
||||
|
||||
func TestObserveModeForwardsAClientABlocklistDeniesAndAlertsIt(t *testing.T) {
|
||||
func TestObserveModeForwardsAClientABlocklistDeniesAndAlertsIt(
|
||||
t *testing.T,
|
||||
) {
|
||||
t.Parallel()
|
||||
|
||||
s, server, queue := startWithLookups(t, map[string]string{
|
||||
@@ -203,7 +226,9 @@ func TestObserveModeForwardsAClientABlocklistDeniesAndAlertsIt(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlocklistLimitTakesPartInTheLowestPercentageOfEveryLimit(t *testing.T) {
|
||||
func TestBlocklistLimitTakesPartInTheLowestPercentageOfEveryLimit(
|
||||
t *testing.T,
|
||||
) {
|
||||
t.Parallel()
|
||||
|
||||
for _, tc := range []struct {
|
||||
@@ -238,13 +263,19 @@ func TestBlocklistLimitTakesPartInTheLowestPercentageOfEveryLimit(t *testing.T)
|
||||
tc.want)
|
||||
|
||||
if line.LimitHit != tc.limitHit {
|
||||
t.Errorf("log line has limit_hit %q, want %q", line.LimitHit, tc.limitHit)
|
||||
t.Errorf(
|
||||
"log line has limit_hit %q, want %q",
|
||||
line.LimitHit,
|
||||
tc.limitHit,
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestASNLimitPercentFileCountsAsTheSettingDoesTheLowerWinning(t *testing.T) {
|
||||
func TestASNLimitPercentFileCountsAsTheSettingDoesTheLowerWinning(
|
||||
t *testing.T,
|
||||
) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
@@ -288,7 +319,11 @@ func TestASNLimitPercentFileCountsAsTheSettingDoesTheLowerWinning(t *testing.T)
|
||||
maps.Copy(env, tc.env)
|
||||
|
||||
s, server, _ := startWithLookups(t, env)
|
||||
loadLists(t, server, map[string][]string{asnURL: {"# by AS number", tc.file}})
|
||||
loadLists(
|
||||
t,
|
||||
server,
|
||||
map[string][]string{asnURL: {"# by AS number", tc.file}},
|
||||
)
|
||||
|
||||
line := s.uploadFrom(fromDE)
|
||||
wantPercent(t, "rate_limit_percent",
|
||||
@@ -313,13 +348,21 @@ func TestEachBlocklistThatListsAClientRaisesAnAlertOncePerCooldownAndIsCounted(
|
||||
blocklistAction: actionLog,
|
||||
metricsToken: token,
|
||||
})
|
||||
loadLists(t, server, map[string][]string{dropURL: {fromDE}, torURL: {fromDE}})
|
||||
loadLists(
|
||||
t,
|
||||
server,
|
||||
map[string][]string{dropURL: {fromDE}, torURL: {fromDE}},
|
||||
)
|
||||
|
||||
// The second request's alerts are repeats, which the cooldown holds
|
||||
// back.
|
||||
for range 2 {
|
||||
wantReputation(t, s.get(fromDE, http.StatusOK, requestlog.ActionForward),
|
||||
dropURL, torURL)
|
||||
wantReputation(
|
||||
t,
|
||||
s.get(fromDE, http.StatusOK, requestlog.ActionForward),
|
||||
dropURL,
|
||||
torURL,
|
||||
)
|
||||
}
|
||||
|
||||
hit := func(source string) alerts.Alert {
|
||||
@@ -340,7 +383,10 @@ func TestEachBlocklistThatListsAClientRaisesAnAlertOncePerCooldownAndIsCounted(
|
||||
wantAlerts(t, queue, hit(dropURL), hit(torURL))
|
||||
|
||||
if queue.Suppressed() != 2 {
|
||||
t.Errorf("%d alerts held back, want the second request's 2", queue.Suppressed())
|
||||
t.Errorf(
|
||||
"%d alerts held back, want the second request's 2",
|
||||
queue.Suppressed(),
|
||||
)
|
||||
}
|
||||
|
||||
// Each list's hits, none of its fetches failed, and when its copy was
|
||||
@@ -360,8 +406,12 @@ func TestEachBlocklistThatListsAClientRaisesAnAlertOncePerCooldownAndIsCounted(
|
||||
}
|
||||
|
||||
wantMetric(t, metrics, "sww_reputation_failures_total"+labels, 0)
|
||||
wantMetric(t, metrics, "sww_reputation_last_fetch_timestamp_seconds"+labels,
|
||||
want.fetched)
|
||||
wantMetric(
|
||||
t,
|
||||
metrics,
|
||||
"sww_reputation_last_fetch_timestamp_seconds"+labels,
|
||||
want.fetched,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,7 +468,8 @@ func TestEachReputationActionForAClientADNSBLZoneLists(t *testing.T) {
|
||||
dnsblResolver: noResolver, reputationAction: tc.action,
|
||||
})
|
||||
listedBy := map[string][]string{
|
||||
fromDE: {dnsblZone, otherZone}, fromKP: {otherZone}, unplaced: nil,
|
||||
fromDE: {dnsblZone, otherZone}, fromKP: {otherZone},
|
||||
unplaced: nil,
|
||||
}
|
||||
loadVerdicts(server, listedBy)
|
||||
|
||||
@@ -427,14 +478,19 @@ func TestEachReputationActionForAClientADNSBLZoneLists(t *testing.T) {
|
||||
line := s.get(from, tc.statuses[i], tc.actions[i])
|
||||
// In the order SWW_DNSBL_ZONES names them.
|
||||
wantReputation(t, line, listedBy[from]...)
|
||||
wantPercent(t, "rate_limit_percent", line.RateLimitPercent,
|
||||
line.RateLimitPercentSetting, tc.percent)
|
||||
wantPercent(t, "rate_limit_percent",
|
||||
line.RateLimitPercent, line.RateLimitPercentSetting,
|
||||
tc.percent)
|
||||
|
||||
// A request refused for the verdict is not counted.
|
||||
counted := line.fields["counts"] != nil
|
||||
if counted != (tc.actions[i] != denied) {
|
||||
t.Errorf("request from %s counted %t, logged %s", from, counted,
|
||||
tc.actions[i])
|
||||
t.Errorf(
|
||||
"request from %s counted %t, logged %s",
|
||||
from,
|
||||
counted,
|
||||
tc.actions[i],
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -450,7 +506,8 @@ func TestEachReputationActionForAClientADNSBLZoneLists(t *testing.T) {
|
||||
|
||||
// A refusal for the verdict makes no ban, and every client had its
|
||||
// verdicts, so no zone was asked.
|
||||
if held := server.Ledger.Snapshot(); tc.action == actionDeny && len(held) != 0 {
|
||||
if held := server.Ledger.Snapshot(); tc.action == actionDeny &&
|
||||
len(held) != 0 {
|
||||
t.Errorf("bans %+v, want none", held)
|
||||
}
|
||||
|
||||
@@ -469,18 +526,27 @@ func TestDNSBLZonesComeAfterTheBlocklistsAndSkipAllowNets(t *testing.T) {
|
||||
reputationAction: actionDeny, allowNets: fromDE,
|
||||
})
|
||||
loadLists(t, server, map[string][]string{dropURL: {fromKP}})
|
||||
loadVerdicts(server, map[string][]string{fromKP: {dnsblZone}, fromDE: {dnsblZone}})
|
||||
loadVerdicts(
|
||||
server,
|
||||
map[string][]string{fromKP: {dnsblZone}, fromDE: {dnsblZone}},
|
||||
)
|
||||
|
||||
// The blocklist refuses fromKP before its verdict is looked at, and
|
||||
// fromDE, in SWW_ALLOW_NETS, is not checked at all: neither is noted
|
||||
// for the zone, nor alerted, nor asked about.
|
||||
wantReputation(t, s.get(fromKP, http.StatusForbidden, requestlog.ActionClientDenied),
|
||||
dropURL)
|
||||
wantReputation(
|
||||
t,
|
||||
s.get(fromKP, http.StatusForbidden, requestlog.ActionClientDenied),
|
||||
dropURL,
|
||||
)
|
||||
wantReputation(t, s.get(fromDE, http.StatusOK, requestlog.ActionForward))
|
||||
|
||||
waiting := queue.Snapshot().Waiting[alerts.DestinationWebhook]
|
||||
if len(waiting) != 1 || waiting[0].Detail["source"] != dropURL {
|
||||
t.Errorf("alerts waiting %+v, want the blocklist's reputation_hit alone", waiting)
|
||||
t.Errorf(
|
||||
"alerts waiting %+v, want the blocklist's reputation_hit alone",
|
||||
waiting,
|
||||
)
|
||||
}
|
||||
|
||||
if queries := server.DNSBL.Queries(dnsblZone); queries != 0 {
|
||||
@@ -522,13 +588,17 @@ func TestReputationSourcesTagTheRequestWhateverTheirAction(t *testing.T) {
|
||||
s, server, _ := startWithLookups(t, tc.env)
|
||||
tc.load(server)
|
||||
|
||||
wantTags(t, s.get(fromDE, http.StatusOK, requestlog.ActionForward),
|
||||
wantTags(
|
||||
t,
|
||||
s.get(fromDE, http.StatusOK, requestlog.ActionForward),
|
||||
tc.want...)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestObserveModeForwardsAClientADNSBLZoneDeniesAndAlertsIt(t *testing.T) {
|
||||
func TestObserveModeForwardsAClientADNSBLZoneDeniesAndAlertsIt(
|
||||
t *testing.T,
|
||||
) {
|
||||
t.Parallel()
|
||||
|
||||
s, server, queue := startWithLookups(t, map[string]string{
|
||||
@@ -547,7 +617,9 @@ func TestObserveModeForwardsAClientADNSBLZoneDeniesAndAlertsIt(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestReputationLimitTakesPartInTheLowestPercentageOfEveryLimit(t *testing.T) {
|
||||
func TestReputationLimitTakesPartInTheLowestPercentageOfEveryLimit(
|
||||
t *testing.T,
|
||||
) {
|
||||
t.Parallel()
|
||||
|
||||
for _, tc := range []struct {
|
||||
@@ -563,27 +635,37 @@ func TestReputationLimitTakesPartInTheLowestPercentageOfEveryLimit(t *testing.T)
|
||||
{actionLog, limitQuarter, "25 from " + reputationAction},
|
||||
{actionLog, actionLog, none},
|
||||
} {
|
||||
t.Run(tc.blocklistAction+" "+tc.reputationAction, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run(
|
||||
tc.blocklistAction+" "+tc.reputationAction,
|
||||
func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
s, server, _ := startWithLookups(t, map[string]string{
|
||||
blocklistURLs: dropURL, blocklistAction: tc.blocklistAction,
|
||||
dnsblZones: dnsblZone, dnsblResolver: noResolver,
|
||||
reputationAction: tc.reputationAction,
|
||||
})
|
||||
loadLists(t, server, map[string][]string{dropURL: {fromDE}})
|
||||
loadVerdicts(server, map[string][]string{fromDE: {dnsblZone}})
|
||||
s, server, _ := startWithLookups(t, map[string]string{
|
||||
blocklistURLs: dropURL, blocklistAction: tc.blocklistAction,
|
||||
dnsblZones: dnsblZone, dnsblResolver: noResolver,
|
||||
reputationAction: tc.reputationAction,
|
||||
})
|
||||
loadLists(t, server, map[string][]string{dropURL: {fromDE}})
|
||||
loadVerdicts(
|
||||
server,
|
||||
map[string][]string{fromDE: {dnsblZone}},
|
||||
)
|
||||
|
||||
// Named by the blocklist, then by the zone.
|
||||
line := s.get(fromDE, http.StatusOK, requestlog.ActionForward)
|
||||
wantReputation(t, line, dropURL, dnsblZone)
|
||||
wantPercent(t, "rate_limit_percent",
|
||||
line.RateLimitPercent, line.RateLimitPercentSetting,
|
||||
tc.want)
|
||||
wantPercent(t, "bytes_limit_percent",
|
||||
line.BytesLimitPercent, line.BytesLimitPercentSetting,
|
||||
tc.want)
|
||||
})
|
||||
// Named by the blocklist, then by the zone.
|
||||
line := s.get(
|
||||
fromDE,
|
||||
http.StatusOK,
|
||||
requestlog.ActionForward,
|
||||
)
|
||||
wantReputation(t, line, dropURL, dnsblZone)
|
||||
wantPercent(t, "rate_limit_percent",
|
||||
line.RateLimitPercent, line.RateLimitPercentSetting,
|
||||
tc.want)
|
||||
wantPercent(t, "bytes_limit_percent",
|
||||
line.BytesLimitPercent, line.BytesLimitPercentSetting,
|
||||
tc.want)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -603,8 +685,12 @@ func TestEachZoneThatListsAClientRaisesAnAlertOncePerCooldownAndIsCounted(
|
||||
// The second request's alerts are repeats, which the cooldown holds
|
||||
// back.
|
||||
for range 2 {
|
||||
wantReputation(t, s.get(fromDE, http.StatusOK, requestlog.ActionForward),
|
||||
dnsblZone, otherZone)
|
||||
wantReputation(
|
||||
t,
|
||||
s.get(fromDE, http.StatusOK, requestlog.ActionForward),
|
||||
dnsblZone,
|
||||
otherZone,
|
||||
)
|
||||
}
|
||||
|
||||
hit := func(zone string) alerts.Alert {
|
||||
@@ -625,7 +711,10 @@ func TestEachZoneThatListsAClientRaisesAnAlertOncePerCooldownAndIsCounted(
|
||||
wantAlerts(t, queue, hit(dnsblZone), hit(otherZone))
|
||||
|
||||
if queue.Suppressed() != 2 {
|
||||
t.Errorf("%d alerts held back, want the second request's 2", queue.Suppressed())
|
||||
t.Errorf(
|
||||
"%d alerts held back, want the second request's 2",
|
||||
queue.Suppressed(),
|
||||
)
|
||||
}
|
||||
|
||||
// Each zone's hits, and its queries and their failures, none, since
|
||||
@@ -656,12 +745,19 @@ func TestZoneKeyIsMaskedInTheLogTheAlertAndTheMetrics(t *testing.T) {
|
||||
})
|
||||
loadVerdicts(server, map[string][]string{fromDE: {keyed}, unplaced: nil})
|
||||
|
||||
wantReputation(t, s.get(fromDE, http.StatusOK, requestlog.ActionForward), masked)
|
||||
wantReputation(
|
||||
t,
|
||||
s.get(fromDE, http.StatusOK, requestlog.ActionForward),
|
||||
masked,
|
||||
)
|
||||
|
||||
waiting := queue.Snapshot().Waiting[alerts.DestinationWebhook]
|
||||
if len(waiting) != 1 || waiting[0].Detail["source"] != masked {
|
||||
t.Errorf("alerts waiting %+v, want a reputation_hit alert from %s", waiting,
|
||||
masked)
|
||||
t.Errorf(
|
||||
"alerts waiting %+v, want a reputation_hit alert from %s",
|
||||
waiting,
|
||||
masked,
|
||||
)
|
||||
}
|
||||
|
||||
metrics := s.scrape()
|
||||
@@ -677,7 +773,9 @@ func TestZoneKeyIsMaskedInTheLogTheAlertAndTheMetrics(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequestFromAClientWithoutAVerdictHasTheZoneAskedAboutIt(t *testing.T) {
|
||||
func TestRequestFromAClientWithoutAVerdictHasTheZoneAskedAboutIt(
|
||||
t *testing.T,
|
||||
) {
|
||||
t.Parallel()
|
||||
|
||||
s, server, _ := startWithLookups(t, map[string]string{
|
||||
@@ -693,7 +791,11 @@ func TestRequestFromAClientWithoutAVerdictHasTheZoneAskedAboutIt(t *testing.T) {
|
||||
// request finds the query under way, or the zone left alone after it
|
||||
// failed, since nothing answers at noResolver.
|
||||
for range 2 {
|
||||
wantReputation(t, s.get(fromDE, http.StatusOK, requestlog.ActionForward), otherZone)
|
||||
wantReputation(
|
||||
t,
|
||||
s.get(fromDE, http.StatusOK, requestlog.ActionForward),
|
||||
otherZone,
|
||||
)
|
||||
}
|
||||
|
||||
if queries := server.DNSBL.Queries(dnsblZone); queries != 1 {
|
||||
@@ -725,17 +827,23 @@ const abuseIPDBURL = "abuseipdb://stand-in/api/v2/check"
|
||||
type abuseIPDBStandIn struct{}
|
||||
|
||||
// RoundTrip answers req with the score 100.
|
||||
func (abuseIPDBStandIn) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
func (abuseIPDBStandIn) RoundTrip(
|
||||
req *http.Request,
|
||||
) (*http.Response, error) {
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Status: "200 OK",
|
||||
Header: http.Header{},
|
||||
Body: io.NopCloser(strings.NewReader(`{"data":{"abuseConfidenceScore":100}}`)),
|
||||
Request: req,
|
||||
Body: io.NopCloser(
|
||||
strings.NewReader(`{"data":{"abuseConfidenceScore":100}}`),
|
||||
),
|
||||
Request: req,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func TestOnlyAClientThatHasCommittedAnOffenceIsCheckedWithAbuseIPDB(t *testing.T) {
|
||||
func TestOnlyAClientThatHasCommittedAnOffenceIsCheckedWithAbuseIPDB(
|
||||
t *testing.T,
|
||||
) {
|
||||
t.Parallel()
|
||||
|
||||
forward := requestlog.ActionForward
|
||||
@@ -766,7 +874,9 @@ func TestOnlyAClientThatHasCommittedAnOffenceIsCheckedWithAbuseIPDB(t *testing.T
|
||||
wantAbuseIPDBChecks(t, server, 1)
|
||||
}
|
||||
|
||||
func TestIPv6ClientCostsOneAbuseIPDBCheckWhicheverOfItsAddressesSends(t *testing.T) {
|
||||
func TestIPv6ClientCostsOneAbuseIPDBCheckWhicheverOfItsAddressesSends(
|
||||
t *testing.T,
|
||||
) {
|
||||
t.Parallel()
|
||||
|
||||
forward := requestlog.ActionForward
|
||||
@@ -775,7 +885,10 @@ func TestIPv6ClientCostsOneAbuseIPDBCheckWhicheverOfItsAddressesSends(t *testing
|
||||
// of its own.
|
||||
var addresses []string
|
||||
for i := 1; i < 16; i++ {
|
||||
addresses = append(addresses, fmt.Sprintf("2001:db8:1:2:%x::9", i<<12))
|
||||
addresses = append(
|
||||
addresses,
|
||||
fmt.Sprintf("2001:db8:1:2:%x::9", i<<12),
|
||||
)
|
||||
}
|
||||
|
||||
s, clk, server := startWithClock(t, "", map[string]string{
|
||||
@@ -789,7 +902,11 @@ func TestIPv6ClientCostsOneAbuseIPDBCheckWhicheverOfItsAddressesSends(t *testing
|
||||
s.get(addresses[0], http.StatusOK, forward)
|
||||
}
|
||||
|
||||
s.get(addresses[0], http.StatusForbidden, requestlog.ActionClientIsRateLimited)
|
||||
s.get(
|
||||
addresses[0],
|
||||
http.StatusForbidden,
|
||||
requestlog.ActionClientIsRateLimited,
|
||||
)
|
||||
clk.advance(time.Hour)
|
||||
|
||||
// Once the ban has ended, which set its counters back to zero, a
|
||||
@@ -808,7 +925,9 @@ const (
|
||||
blockedPath = "/blocked"
|
||||
)
|
||||
|
||||
func TestClientRefusedForAnOffenceIsCheckedWithAbuseIPDBAtItsNextRequest(t *testing.T) {
|
||||
func TestClientRefusedForAnOffenceIsCheckedWithAbuseIPDBAtItsNextRequest(
|
||||
t *testing.T,
|
||||
) {
|
||||
t.Parallel()
|
||||
|
||||
for _, tc := range []struct {
|
||||
@@ -851,7 +970,11 @@ func TestClientRefusedForAnOffenceIsCheckedWithAbuseIPDBAtItsNextRequest(t *test
|
||||
wantAbuseIPDBChecks(t, server, 0)
|
||||
|
||||
if got := historyOf(t, server, client).Offences; got != tc.want {
|
||||
t.Errorf("history counts the offences %+v, want %+v", got, tc.want)
|
||||
t.Errorf(
|
||||
"history counts the offences %+v, want %+v",
|
||||
got,
|
||||
tc.want,
|
||||
)
|
||||
}
|
||||
|
||||
// Its next request, once any ban for a clear sign of attack
|
||||
@@ -863,7 +986,9 @@ func TestClientRefusedForAnOffenceIsCheckedWithAbuseIPDBAtItsNextRequest(t *test
|
||||
}
|
||||
}
|
||||
|
||||
func TestEachReputationActionForAClientAbuseIPDBScoresAtOrOverTheMinimum(t *testing.T) {
|
||||
func TestEachReputationActionForAClientAbuseIPDBScoresAtOrOverTheMinimum(
|
||||
t *testing.T,
|
||||
) {
|
||||
t.Parallel()
|
||||
|
||||
forward, denied := requestlog.ActionForward, requestlog.ActionClientDenied
|
||||
@@ -911,7 +1036,11 @@ func TestEachReputationActionForAClientAbuseIPDBScoresAtOrOverTheMinimum(t *test
|
||||
// A request refused for the score is not counted.
|
||||
counted := line.fields["counts"] != nil
|
||||
if counted != (tc.actions[i] != denied) {
|
||||
t.Errorf("request counted %t, logged %s", counted, tc.actions[i])
|
||||
t.Errorf(
|
||||
"request counted %t, logged %s",
|
||||
counted,
|
||||
tc.actions[i],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -925,7 +1054,8 @@ func TestEachReputationActionForAClientAbuseIPDBScoresAtOrOverTheMinimum(t *test
|
||||
}
|
||||
|
||||
// A refusal for the score makes no ban.
|
||||
if held := server.Ledger.Snapshot(); tc.action == actionDeny && len(held) != 0 {
|
||||
if held := server.Ledger.Snapshot(); tc.action == actionDeny &&
|
||||
len(held) != 0 {
|
||||
t.Errorf("bans %+v, want none", held)
|
||||
}
|
||||
})
|
||||
@@ -944,8 +1074,11 @@ func TestAbuseIPDBHitRaisesAnAlertWithTheScoreOncePerCooldownAndIsCounted(
|
||||
|
||||
// The second request's alert is a repeat, which the cooldown holds back.
|
||||
for range 2 {
|
||||
wantReputation(t, s.get(fromDE, http.StatusOK, requestlog.ActionForward),
|
||||
abuseipdb)
|
||||
wantReputation(
|
||||
t,
|
||||
s.get(fromDE, http.StatusOK, requestlog.ActionForward),
|
||||
abuseipdb,
|
||||
)
|
||||
}
|
||||
|
||||
// The alert is made as a DNSBL zone's is, with the score besides.
|
||||
@@ -954,8 +1087,12 @@ func TestAbuseIPDBHitRaisesAnAlertWithTheScoreOncePerCooldownAndIsCounted(
|
||||
waiting[0].Reason != "scored by AbuseIPDB at or over SWW_ABUSEIPDB_MIN_SCORE" ||
|
||||
waiting[0].Detail["source"] != abuseipdb || waiting[0].Detail["score"] != int64(90) ||
|
||||
queue.Suppressed() != 1 {
|
||||
t.Errorf("alerts waiting %+v, %d held back, want AbuseIPDB's reputation_hit "+
|
||||
"with the score 90, and 1", waiting, queue.Suppressed())
|
||||
t.Errorf(
|
||||
"alerts waiting %+v, %d held back, want AbuseIPDB's reputation_hit "+
|
||||
"with the score 90, and 1",
|
||||
waiting,
|
||||
queue.Suppressed(),
|
||||
)
|
||||
}
|
||||
|
||||
// The hits, and the checks, none, since no client committed an
|
||||
@@ -966,7 +1103,12 @@ func TestAbuseIPDBHitRaisesAnAlertWithTheScoreOncePerCooldownAndIsCounted(
|
||||
wantMetric(t, metrics, "sww_reputation_hits_total"+labels, 2)
|
||||
wantMetric(t, metrics, "sww_reputation_queries_total"+labels, 0)
|
||||
wantMetric(t, metrics, "sww_reputation_failures_total"+labels, 0)
|
||||
wantMetric(t, metrics, "sww_reputation_daily_budget_remaining"+labels, 900)
|
||||
wantMetric(
|
||||
t,
|
||||
metrics,
|
||||
"sww_reputation_daily_budget_remaining"+labels,
|
||||
900,
|
||||
)
|
||||
}
|
||||
|
||||
func TestWithoutAnAbuseIPDBKeyNoClientIsCheckedNorAScoreUsed(t *testing.T) {
|
||||
@@ -996,7 +1138,8 @@ func TestBanNotesNameEachReputationSourceThatListedTheClient(t *testing.T) {
|
||||
|
||||
score := int64(90)
|
||||
listed := []bans.ReputationHit{
|
||||
{Source: dropURL}, {Source: dnsblZone}, {Source: abuseipdb, Score: &score},
|
||||
{Source: dropURL}, {Source: dnsblZone},
|
||||
{Source: abuseipdb, Score: &score},
|
||||
}
|
||||
|
||||
for _, tc := range []struct {
|
||||
@@ -1024,8 +1167,12 @@ func TestBanNotesNameEachReputationSourceThatListedTheClient(t *testing.T) {
|
||||
// Every source lists client, and none otherClient, whose score is
|
||||
// under SWW_ABUSEIPDB_MIN_SCORE, 75 by default.
|
||||
loadLists(t, server, map[string][]string{dropURL: {client}})
|
||||
loadVerdicts(server, map[string][]string{client: {dnsblZone}, otherClient: nil})
|
||||
loadScores(server, map[string]int64{client: score, otherClient: 74})
|
||||
loadVerdicts(server, map[string][]string{
|
||||
client: {dnsblZone}, otherClient: nil,
|
||||
})
|
||||
loadScores(server, map[string]int64{
|
||||
client: score, otherClient: 74,
|
||||
})
|
||||
|
||||
for _, banned := range []struct {
|
||||
from string
|
||||
@@ -1033,10 +1180,20 @@ func TestBanNotesNameEachReputationSourceThatListedTheClient(t *testing.T) {
|
||||
}{{client, listed}, {otherClient, nil}} {
|
||||
tc.ban(s, banned.from)
|
||||
|
||||
held := server.Ledger.Bans(netip.MustParsePrefix(banned.from + "/32"))
|
||||
if len(held) != 1 || !reflect.DeepEqual(held[0].Notes.Reputation, banned.want) {
|
||||
t.Errorf("bans of %s %+v, want one whose notes have the reputation %+v",
|
||||
banned.from, held, banned.want)
|
||||
held := server.Ledger.Bans(
|
||||
netip.MustParsePrefix(banned.from + "/32"),
|
||||
)
|
||||
if len(held) != 1 ||
|
||||
!reflect.DeepEqual(
|
||||
held[0].Notes.Reputation,
|
||||
banned.want,
|
||||
) {
|
||||
t.Errorf(
|
||||
"bans of %s %+v, want one whose notes have the reputation %+v",
|
||||
banned.from,
|
||||
held,
|
||||
banned.want,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1050,9 +1207,13 @@ func TestBanNotesNameEachReputationSourceThatListedTheClient(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
if want := [][]bans.ReputationHit{listed, nil}; !reflect.DeepEqual(alerted, want) {
|
||||
t.Errorf("the ban alerts' notes have the reputation %+v, want %+v",
|
||||
alerted, want)
|
||||
want := [][]bans.ReputationHit{listed, nil}
|
||||
if !reflect.DeepEqual(alerted, want) {
|
||||
t.Errorf(
|
||||
"the ban alerts' notes have the reputation %+v, want %+v",
|
||||
alerted,
|
||||
want,
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1076,13 +1237,20 @@ func verdictsFetched() time.Time {
|
||||
// client from each zone listedBy gives for it, and one that does not from
|
||||
// each other zone.
|
||||
func loadVerdicts(server *waf.Server, listedBy map[string][]string) {
|
||||
verdicts := make([]reputation.Verdict, 0, len(listedBy)*len(server.DNSBL.Zones()))
|
||||
verdicts := make(
|
||||
[]reputation.Verdict,
|
||||
0,
|
||||
len(listedBy)*len(server.DNSBL.Zones()),
|
||||
)
|
||||
|
||||
for client, zones := range listedBy {
|
||||
for _, zone := range server.DNSBL.Zones() {
|
||||
verdicts = append(verdicts, reputation.Verdict{
|
||||
Zone: zone, Client: netip.MustParseAddr(client),
|
||||
Listed: slices.Contains(zones, zone), Fetched: verdictsFetched(),
|
||||
Listed: slices.Contains(
|
||||
zones,
|
||||
zone,
|
||||
), Fetched: verdictsFetched(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1117,7 +1285,11 @@ func wantAbuseIPDBChecks(t *testing.T, server *waf.Server, want int) {
|
||||
|
||||
// loadLists puts copies of lists into server's lists, by URL, each with
|
||||
// its lines, fetched at listsFetched, as lists.json would at start.
|
||||
func loadLists(t *testing.T, server *waf.Server, copies map[string][]string) {
|
||||
func loadLists(
|
||||
t *testing.T,
|
||||
server *waf.Server,
|
||||
copies map[string][]string,
|
||||
) {
|
||||
t.Helper()
|
||||
|
||||
lists := make([]reputation.List, 0, len(copies))
|
||||
@@ -1139,6 +1311,10 @@ func wantReputation(t *testing.T, line logLine, want ...string) {
|
||||
t.Helper()
|
||||
|
||||
if !slices.Equal(line.Reputation, want) {
|
||||
t.Errorf("log line has reputation %v, want %v", line.Reputation, want)
|
||||
t.Errorf(
|
||||
"log line has reputation %v, want %v",
|
||||
line.Reputation,
|
||||
want,
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user