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
+185
-48
@@ -75,7 +75,9 @@ func fillHeaders(req *http.Request, total int) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSixteenThousandCookiesAreRefusedBeforeTheCoreRuleSetReadsThem(t *testing.T) {
|
||||
func TestSixteenThousandCookiesAreRefusedBeforeTheCoreRuleSetReadsThem(
|
||||
t *testing.T,
|
||||
) {
|
||||
t.Parallel()
|
||||
|
||||
s := startWAF(t, "", map[string]string{wafMode: block})
|
||||
@@ -90,7 +92,10 @@ func TestSixteenThousandCookiesAreRefusedBeforeTheCoreRuleSetReadsThem(t *testin
|
||||
wantLine(t, line, tooLarge, requestlog.ActionRequestHeaderTooLarge)
|
||||
|
||||
if line.DurationCoreRuleSetMS != nil {
|
||||
t.Errorf("the Core Rule Set read the request, for %f ms", *line.DurationCoreRuleSetMS)
|
||||
t.Errorf(
|
||||
"the Core Rule Set read the request, for %f ms",
|
||||
*line.DurationCoreRuleSetMS,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,8 +107,12 @@ func TestHeaderLimitRefusalNamesItsHeuristicCountsAndLimits(t *testing.T) {
|
||||
fillHeaders(req, headerFieldLimit+1)
|
||||
do(t, req)
|
||||
|
||||
wantHeuristic(t, s.out.requestLine(t), "header_limits",
|
||||
`{"fields": 101, "fields_limit": 100, "cookies": 0, "cookies_limit": 50}`)
|
||||
wantHeuristic(
|
||||
t,
|
||||
s.out.requestLine(t),
|
||||
"header_limits",
|
||||
`{"fields": 101, "fields_limit": 100, "cookies": 0, "cookies_limit": 50}`,
|
||||
)
|
||||
}
|
||||
|
||||
func TestEachHeaderLimitRefusesARequestOverIt(t *testing.T) {
|
||||
@@ -160,7 +169,12 @@ func TestEachHeaderLimitRefusesARequestOverIt(t *testing.T) {
|
||||
|
||||
if tc.refused {
|
||||
wantDisposition(t, got, tooLarge, waf.DispositionDeny)
|
||||
wantLine(t, line, tooLarge, requestlog.ActionRequestHeaderTooLarge)
|
||||
wantLine(
|
||||
t,
|
||||
line,
|
||||
tooLarge,
|
||||
requestlog.ActionRequestHeaderTooLarge,
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
@@ -169,7 +183,9 @@ func TestEachHeaderLimitRefusesARequestOverIt(t *testing.T) {
|
||||
wantLine(t, line, http.StatusOK, requestlog.ActionForward)
|
||||
|
||||
if line.DurationCoreRuleSetMS == nil {
|
||||
t.Error("the Core Rule Set did not read a request within the limits")
|
||||
t.Error(
|
||||
"the Core Rule Set did not read a request within the limits",
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -198,7 +214,11 @@ func TestHeaderLimitsHoldWhateverTheCoreRuleSetSettingsSay(t *testing.T) {
|
||||
func TestClientInTheAllowNetsSkipsTheHeaderLimits(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
s := startWAF(t, "", map[string]string{wafMode: block, allowNets: client})
|
||||
s := startWAF(
|
||||
t,
|
||||
"",
|
||||
map[string]string{wafMode: block, allowNets: client},
|
||||
)
|
||||
req := s.question(siteA, client, "/")
|
||||
req.Header.Set(cookieHeader, cookies(cookieLimit+1))
|
||||
|
||||
@@ -219,8 +239,12 @@ func TestObserveModeAllowsARequestOverTheHeaderLimitsUnread(t *testing.T) {
|
||||
line := s.out.requestLine(t)
|
||||
if line.WouldAction != requestlog.ActionRequestHeaderTooLarge ||
|
||||
line.DurationCoreRuleSetMS != nil {
|
||||
t.Errorf("would_action %q and duration_core_rule_set_ms %v, want %q and none",
|
||||
line.WouldAction, line.DurationCoreRuleSetMS, requestlog.ActionRequestHeaderTooLarge)
|
||||
t.Errorf(
|
||||
"would_action %q and duration_core_rule_set_ms %v, want %q and none",
|
||||
line.WouldAction,
|
||||
line.DurationCoreRuleSetMS,
|
||||
requestlog.ActionRequestHeaderTooLarge,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,7 +259,12 @@ func TestTheRateLimitsDoNotCountRequestsOverTheHeaderLimits(t *testing.T) {
|
||||
wantDisposition(t, do(t, req), tooLarge, waf.DispositionDeny)
|
||||
}
|
||||
|
||||
wantDisposition(t, s.ask(client, "/"), http.StatusOK, waf.DispositionAllow)
|
||||
wantDisposition(
|
||||
t,
|
||||
s.ask(client, "/"),
|
||||
http.StatusOK,
|
||||
waf.DispositionAllow,
|
||||
)
|
||||
}
|
||||
|
||||
func TestTheHeaderLimitsComeBeforeEveryCheckThatRefuses(t *testing.T) {
|
||||
@@ -310,7 +339,12 @@ func TestTheHeaderLimitsComeBeforeEveryCheckThatRefuses(t *testing.T) {
|
||||
req.Header.Set(cookieHeader, cookies(cookieLimit+1))
|
||||
|
||||
wantDisposition(t, do(t, req), tooLarge, waf.DispositionDeny)
|
||||
wantLine(t, s.out.requestLine(t), tooLarge, requestlog.ActionRequestHeaderTooLarge)
|
||||
wantLine(
|
||||
t,
|
||||
s.out.requestLine(t),
|
||||
tooLarge,
|
||||
requestlog.ActionRequestHeaderTooLarge,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -327,9 +361,12 @@ func TestARequestOverTheHeaderLimitsRunsNoOtherCheck(t *testing.T) {
|
||||
})
|
||||
s := startWAF(t, "", map[string]string{
|
||||
lookupSource: fileSource, lookupDBPath: path,
|
||||
tagLists: "vpn=" + vpnList,
|
||||
routeWatchURL: startRouteWatch(t, map[string]standInRoute{client: {prefixDE, 64496}}),
|
||||
dnsblZones: dnsblZone, reputationAction: actionLog,
|
||||
tagLists: "vpn=" + vpnList,
|
||||
routeWatchURL: startRouteWatch(
|
||||
t,
|
||||
map[string]standInRoute{client: {prefixDE, 64496}},
|
||||
),
|
||||
dnsblZones: dnsblZone, reputationAction: actionLog,
|
||||
rulesDir: writeRules(t, "noted path log ^/\n"),
|
||||
wafMode: block,
|
||||
})
|
||||
@@ -351,19 +388,34 @@ func TestARequestOverTheHeaderLimitsRunsNoOtherCheck(t *testing.T) {
|
||||
|
||||
if lines[0].RuleIDs != nil || lines[0].Counts != (ratelimit.Counts{}) ||
|
||||
lines[0].DurationCoreRuleSetMS != nil {
|
||||
t.Errorf("rule ids %v, counts %+v and Core Rule Set time %v, want none",
|
||||
lines[0].RuleIDs, lines[0].Counts, lines[0].DurationCoreRuleSetMS)
|
||||
t.Errorf(
|
||||
"rule ids %v, counts %+v and Core Rule Set time %v, want none",
|
||||
lines[0].RuleIDs,
|
||||
lines[0].Counts,
|
||||
lines[0].DurationCoreRuleSetMS,
|
||||
)
|
||||
}
|
||||
|
||||
// The request within the limits shows that each check leaves its mark.
|
||||
wantTags(t, lines[1],
|
||||
"asn=64496", "country=DE", "dnsbl="+dnsblZone, "exploitsignature", "vpn")
|
||||
wantTags(
|
||||
t,
|
||||
lines[1],
|
||||
"asn=64496",
|
||||
"country=DE",
|
||||
"dnsbl="+dnsblZone,
|
||||
"exploitsignature",
|
||||
"vpn",
|
||||
)
|
||||
wantTagsMissing(t, lines[1], "no_answer_yet")
|
||||
|
||||
if lines[1].RuleIDs == nil || lines[1].Counts.Minute != 1 ||
|
||||
lines[1].DurationCoreRuleSetMS == nil {
|
||||
t.Errorf("rule ids %v, counts %+v and Core Rule Set time %v, want each",
|
||||
lines[1].RuleIDs, lines[1].Counts, lines[1].DurationCoreRuleSetMS)
|
||||
t.Errorf(
|
||||
"rule ids %v, counts %+v and Core Rule Set time %v, want each",
|
||||
lines[1].RuleIDs,
|
||||
lines[1].Counts,
|
||||
lines[1].DurationCoreRuleSetMS,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -378,17 +430,29 @@ func TestNotWaitingForGeoJSNeverLowersAClientsLimit(t *testing.T) {
|
||||
})
|
||||
countClient(t, s.server, s.clock.Now())
|
||||
|
||||
wantDisposition(t, s.ask(fromDE, "/"), http.StatusForbidden, waf.DispositionDeny)
|
||||
wantDisposition(
|
||||
t,
|
||||
s.ask(fromDE, "/"),
|
||||
http.StatusForbidden,
|
||||
waf.DispositionDeny,
|
||||
)
|
||||
|
||||
line := s.out.requestLine(t)
|
||||
wantLine(t, line, http.StatusForbidden, requestlog.ActionClientIsRateLimited)
|
||||
wantLine(
|
||||
t,
|
||||
line,
|
||||
http.StatusForbidden,
|
||||
requestlog.ActionClientIsRateLimited,
|
||||
)
|
||||
|
||||
if line.RateLimitPercent != nil {
|
||||
t.Errorf("limit percentage %d, want none", *line.RateLimitPercent)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAClientGeoJSPlacedInNoCountryCountsAsUnknownWithoutWaiting(t *testing.T) {
|
||||
func TestAClientGeoJSPlacedInNoCountryCountsAsUnknownWithoutWaiting(
|
||||
t *testing.T,
|
||||
) {
|
||||
t.Parallel()
|
||||
|
||||
// The client's next request takes it over the whole limit, so it does
|
||||
@@ -403,17 +467,29 @@ func TestAClientGeoJSPlacedInNoCountryCountsAsUnknownWithoutWaiting(t *testing.T
|
||||
}})
|
||||
countClient(t, s.server, s.clock.Now())
|
||||
|
||||
wantDisposition(t, s.ask(client, "/"), http.StatusForbidden, waf.DispositionDeny)
|
||||
wantDisposition(
|
||||
t,
|
||||
s.ask(client, "/"),
|
||||
http.StatusForbidden,
|
||||
waf.DispositionDeny,
|
||||
)
|
||||
|
||||
line := s.out.requestLine(t)
|
||||
wantLine(t, line, http.StatusForbidden, requestlog.ActionClientIsRateLimited)
|
||||
wantLine(
|
||||
t,
|
||||
line,
|
||||
http.StatusForbidden,
|
||||
requestlog.ActionClientIsRateLimited,
|
||||
)
|
||||
|
||||
if line.RateLimitPercent == nil || *line.RateLimitPercent != 0 {
|
||||
t.Errorf("limit percentage %v, want 0", line.RateLimitPercent)
|
||||
}
|
||||
}
|
||||
|
||||
func TestObserveModeAsksGeoJSNothingForARequestOverTheHeaderLimits(t *testing.T) {
|
||||
func TestObserveModeAsksGeoJSNothingForARequestOverTheHeaderLimits(
|
||||
t *testing.T,
|
||||
) {
|
||||
t.Parallel()
|
||||
|
||||
geojsURL, asked := startGeoJS(t)
|
||||
@@ -427,8 +503,12 @@ func TestObserveModeAsksGeoJSNothingForARequestOverTheHeaderLimits(t *testing.T)
|
||||
wantDisposition(t, got, http.StatusOK, waf.DispositionAllow)
|
||||
|
||||
if len(asked()) != 0 || got.header.Get("X-Client-Asn") != "" {
|
||||
t.Errorf("GeoJS was asked about %v, and the answer gave the AS number %q, "+
|
||||
"want neither", asked(), got.header.Get("X-Client-Asn"))
|
||||
t.Errorf(
|
||||
"GeoJS was asked about %v, and the answer gave the AS number %q, "+
|
||||
"want neither",
|
||||
asked(),
|
||||
got.header.Get("X-Client-Asn"),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -455,7 +535,12 @@ func askDirectly(
|
||||
) (int, time.Duration) {
|
||||
t.Helper()
|
||||
|
||||
req := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/", http.NoBody)
|
||||
req := httptest.NewRequestWithContext(
|
||||
t.Context(),
|
||||
http.MethodGet,
|
||||
"/",
|
||||
http.NoBody,
|
||||
)
|
||||
req.RemoteAddr = net.JoinHostPort(rproxyPeer, "1234")
|
||||
req.Header.Set(waf.ClientHeader, client)
|
||||
req.Header.Set(requestIDHeader, "the-id")
|
||||
@@ -471,12 +556,20 @@ func askDirectly(
|
||||
|
||||
// banClient bans client at now.
|
||||
func banClient(_ *testing.T, server *waf.Server, now time.Time) {
|
||||
server.Ledger.BanForLimit(netip.MustParsePrefix(client+"/32"), now, bans.Notes{})
|
||||
server.Ledger.BanForLimit(
|
||||
netip.MustParsePrefix(client+"/32"),
|
||||
now,
|
||||
bans.Notes{},
|
||||
)
|
||||
}
|
||||
|
||||
// countClient counts one request from client at now for the rate limits.
|
||||
func countClient(_ *testing.T, server *waf.Server, now time.Time) {
|
||||
server.Limiter.Count(netip.MustParsePrefix(client+"/32"), now, wholeLimit)
|
||||
server.Limiter.Count(
|
||||
netip.MustParsePrefix(client+"/32"),
|
||||
now,
|
||||
wholeLimit,
|
||||
)
|
||||
}
|
||||
|
||||
// listClient puts client on the blocklist at dropURL.
|
||||
@@ -559,13 +652,22 @@ func TestARefusedRequestDoesNotWaitForASlowGeoJS(t *testing.T) {
|
||||
server, out := newWAFInBubble(t, tc.env)
|
||||
tc.prepare(t, server, time.Now())
|
||||
|
||||
status, took := askDirectly(t, server, func(req *http.Request) {
|
||||
req.Header.Set(cookieHeader, tc.cookie)
|
||||
})
|
||||
status, took := askDirectly(
|
||||
t,
|
||||
server,
|
||||
func(req *http.Request) {
|
||||
req.Header.Set(cookieHeader, tc.cookie)
|
||||
},
|
||||
)
|
||||
|
||||
if status != tc.status || took != 0 {
|
||||
t.Errorf("answered %d after %s, want %d at once; output:\n%s",
|
||||
status, took, tc.status, out.text())
|
||||
t.Errorf(
|
||||
"answered %d after %s, want %d at once; output:\n%s",
|
||||
status,
|
||||
took,
|
||||
tc.status,
|
||||
out.text(),
|
||||
)
|
||||
}
|
||||
|
||||
wantLine(t, out.requestLine(t), tc.status, tc.action)
|
||||
@@ -577,7 +679,9 @@ func TestARefusedRequestDoesNotWaitForASlowGeoJS(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAQuestionWaitsForASlowGeoJSOnlyAsLongAsTheDefaultSays(t *testing.T) {
|
||||
func TestAQuestionWaitsForASlowGeoJSOnlyAsLongAsTheDefaultSays(
|
||||
t *testing.T,
|
||||
) {
|
||||
t.Parallel()
|
||||
|
||||
for _, tc := range []struct {
|
||||
@@ -616,11 +720,21 @@ func TestAQuestionWaitsForASlowGeoJSOnlyAsLongAsTheDefaultSays(t *testing.T) {
|
||||
server, out := newWAFInBubble(t, tc.env)
|
||||
tc.prepare(t, server, time.Now())
|
||||
|
||||
status, took := askDirectly(t, server, func(*http.Request) {})
|
||||
status, took := askDirectly(
|
||||
t,
|
||||
server,
|
||||
func(*http.Request) {},
|
||||
)
|
||||
|
||||
if status != tc.status || took != defaultLookupWait {
|
||||
t.Errorf("answered %d after %s, want %d after %s; output:\n%s",
|
||||
status, took, tc.status, defaultLookupWait, out.text())
|
||||
t.Errorf(
|
||||
"answered %d after %s, want %d after %s; output:\n%s",
|
||||
status,
|
||||
took,
|
||||
tc.status,
|
||||
defaultLookupWait,
|
||||
out.text(),
|
||||
)
|
||||
}
|
||||
|
||||
wantLine(t, out.requestLine(t), tc.status, tc.action)
|
||||
@@ -664,7 +778,11 @@ func TestWAFAnswersWithinTheTimeLimitWhileEveryOutsideSourceHangs(
|
||||
synctest.Test(t, func(t *testing.T) {
|
||||
// AbuseIPDB is asked only about a client with an offence.
|
||||
group := netip.MustParsePrefix(client + "/32")
|
||||
server.Limiter.AddToHistory(group, time.Now(), ratelimit.Request{Attack: true})
|
||||
server.Limiter.AddToHistory(
|
||||
group,
|
||||
time.Now(),
|
||||
ratelimit.Request{Attack: true},
|
||||
)
|
||||
|
||||
status, took := askDirectly(t, server, func(req *http.Request) {
|
||||
req.Header.Set("User-Agent", "test")
|
||||
@@ -673,9 +791,17 @@ func TestWAFAnswersWithinTheTimeLimitWhileEveryOutsideSourceHangs(
|
||||
})
|
||||
|
||||
// The wait for GeoJS decides when the answer comes.
|
||||
if status != http.StatusOK || took != defaultLookupWait || took >= answerTimeLimit {
|
||||
t.Errorf("answered %d after %s, want %d after %s, within %s; output:\n%s",
|
||||
status, took, http.StatusOK, defaultLookupWait, answerTimeLimit, out.text())
|
||||
if status != http.StatusOK || took != defaultLookupWait ||
|
||||
took >= answerTimeLimit {
|
||||
t.Errorf(
|
||||
"answered %d after %s, want %d after %s, within %s; output:\n%s",
|
||||
status,
|
||||
took,
|
||||
http.StatusOK,
|
||||
defaultLookupWait,
|
||||
answerTimeLimit,
|
||||
out.text(),
|
||||
)
|
||||
}
|
||||
|
||||
// Each outside source was asked, and none has answered. GeoJS is asked
|
||||
@@ -686,10 +812,17 @@ func TestWAFAnswersWithinTheTimeLimitWhileEveryOutsideSourceHangs(
|
||||
|
||||
// The resolver holds the query until it is released below, so a waf
|
||||
// that waited for the query would answer only once it had failed.
|
||||
queries, failures := server.DNSBL.Queries(dnsblZone), server.DNSBL.Failures(dnsblZone)
|
||||
queries, failures := server.DNSBL.Queries(
|
||||
dnsblZone,
|
||||
), server.DNSBL.Failures(
|
||||
dnsblZone,
|
||||
)
|
||||
if queries != 1 || failures != 0 {
|
||||
t.Errorf("%d queries to the DNS blocklist, %d failed, want 1, none failed",
|
||||
queries, failures)
|
||||
t.Errorf(
|
||||
"%d queries to the DNS blocklist, %d failed, want 1, none failed",
|
||||
queries,
|
||||
failures,
|
||||
)
|
||||
}
|
||||
|
||||
if checked := server.AbuseIPDB.Checked(); checked != 1 {
|
||||
@@ -721,7 +854,11 @@ const (
|
||||
func startHeldResolver(t *testing.T) (string, func()) {
|
||||
t.Helper()
|
||||
|
||||
conn, err := (&net.ListenConfig{}).ListenPacket(t.Context(), "udp", localhost+":0")
|
||||
conn, err := (&net.ListenConfig{}).ListenPacket(
|
||||
t.Context(),
|
||||
"udp",
|
||||
localhost+":0",
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("listen: %v", err)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user