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
+211
-65
@@ -67,8 +67,11 @@ func TestEachByteLimitBansOnceTheResponseHasEnded(t *testing.T) {
|
||||
// 70 bytes are within the limit of 99.
|
||||
line, _ := s.download()
|
||||
if line.LimitHit != "" || line.Offence != "" {
|
||||
t.Errorf("log line has limit_hit %q and offence %q, want neither",
|
||||
line.LimitHit, line.Offence)
|
||||
t.Errorf(
|
||||
"log line has limit_hit %q and offence %q, want neither",
|
||||
line.LimitHit,
|
||||
line.Offence,
|
||||
)
|
||||
}
|
||||
|
||||
// 140 bytes are over it. The response is passed on whole, and
|
||||
@@ -79,18 +82,35 @@ func TestEachByteLimitBansOnceTheResponseHasEnded(t *testing.T) {
|
||||
line, got := s.download()
|
||||
if got.err != nil || len(got.body) != answerBytes ||
|
||||
line.ResponseBytes != answerBytes {
|
||||
t.Errorf("got %d bytes (%v), and the log line has response_bytes %d, "+
|
||||
"want %d", len(got.body), got.err, line.ResponseBytes, answerBytes)
|
||||
t.Errorf(
|
||||
"got %d bytes (%v), and the log line has response_bytes %d, "+
|
||||
"want %d",
|
||||
len(got.body),
|
||||
got.err,
|
||||
line.ResponseBytes,
|
||||
answerBytes,
|
||||
)
|
||||
}
|
||||
|
||||
if line.LimitHit != tc.window+"_bytes" || line.Offence != requestlog.OffenceLimit ||
|
||||
if line.LimitHit != tc.window+"_bytes" ||
|
||||
line.Offence != requestlog.OffenceLimit ||
|
||||
line.BanExpires != expires {
|
||||
t.Errorf("log line has limit_hit %q, offence %q and ban_expires %q, "+
|
||||
"want %s_bytes, limit and %s", line.LimitHit, line.Offence,
|
||||
line.BanExpires, tc.window, expires)
|
||||
t.Errorf(
|
||||
"log line has limit_hit %q, offence %q and ban_expires %q, "+
|
||||
"want %s_bytes, limit and %s",
|
||||
line.LimitHit,
|
||||
line.Offence,
|
||||
line.BanExpires,
|
||||
tc.window,
|
||||
expires,
|
||||
)
|
||||
}
|
||||
|
||||
s.get(client, http.StatusForbidden, requestlog.ActionTemporarilyBanned)
|
||||
s.get(
|
||||
client,
|
||||
http.StatusForbidden,
|
||||
requestlog.ActionTemporarilyBanned,
|
||||
)
|
||||
|
||||
wantMetric(t, s.scrape(), `sww_rate_limit_hits_total{`+
|
||||
`instance="`+alertInstance+`",kind="bytes",window="`+tc.window+`"}`, 1)
|
||||
@@ -101,7 +121,10 @@ func TestEachByteLimitBansOnceTheResponseHasEnded(t *testing.T) {
|
||||
func TestByteLimitBanNamesItsHeuristicWindowCountAndLimit(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
s, _ := startWithAnswers(t, map[string]string{bytesLimitPerMinute: byteLimit})
|
||||
s, _ := startWithAnswers(
|
||||
t,
|
||||
map[string]string{bytesLimitPerMinute: byteLimit},
|
||||
)
|
||||
|
||||
line, _ := s.download()
|
||||
wantAfterAnswerHeuristic(t, line, "", "null")
|
||||
@@ -120,9 +143,16 @@ func TestResponseOverAByteLimitByItselfIsPassedOnWhole(t *testing.T) {
|
||||
|
||||
// The answer's 70 bytes are over the limit of 50 on their own.
|
||||
line, got := s.download()
|
||||
if got.err != nil || len(got.body) != answerBytes || line.LimitHit != minuteBytes {
|
||||
t.Errorf("got %d bytes (%v), and the log line has limit_hit %q, want %d and %s",
|
||||
len(got.body), got.err, line.LimitHit, answerBytes, minuteBytes)
|
||||
if got.err != nil || len(got.body) != answerBytes ||
|
||||
line.LimitHit != minuteBytes {
|
||||
t.Errorf(
|
||||
"got %d bytes (%v), and the log line has limit_hit %q, want %d and %s",
|
||||
len(got.body),
|
||||
got.err,
|
||||
line.LimitHit,
|
||||
answerBytes,
|
||||
minuteBytes,
|
||||
)
|
||||
}
|
||||
|
||||
s.get(client, http.StatusForbidden, requestlog.ActionTemporarilyBanned)
|
||||
@@ -138,13 +168,27 @@ func TestBytesOfAnAnswerThatBreaksOffAreCounted(t *testing.T) {
|
||||
|
||||
// The 70 bytes passed on before the app broke off are over the limit of
|
||||
// 50, and ban the client for an hour.
|
||||
line, got := s.requestWithBody(http.MethodGet, client, "/", "", "", http.StatusOK,
|
||||
requestlog.ActionForward)
|
||||
line, got := s.requestWithBody(
|
||||
http.MethodGet,
|
||||
client,
|
||||
"/",
|
||||
"",
|
||||
"",
|
||||
http.StatusOK,
|
||||
requestlog.ActionForward,
|
||||
)
|
||||
if len(got.body) != answerBytes || line.LimitHit != minuteBytes ||
|
||||
line.BanExpires != expires {
|
||||
t.Errorf("got %d bytes, and the log line has limit_hit %q and ban_expires %q, "+
|
||||
"want %d, %s and %s", len(got.body), line.LimitHit, line.BanExpires,
|
||||
answerBytes, minuteBytes, expires)
|
||||
t.Errorf(
|
||||
"got %d bytes, and the log line has limit_hit %q and ban_expires %q, "+
|
||||
"want %d, %s and %s",
|
||||
len(got.body),
|
||||
line.LimitHit,
|
||||
line.BanExpires,
|
||||
answerBytes,
|
||||
minuteBytes,
|
||||
expires,
|
||||
)
|
||||
}
|
||||
|
||||
s.get(client, http.StatusForbidden, requestlog.ActionTemporarilyBanned)
|
||||
@@ -164,19 +208,33 @@ func TestWebSocketBytesAreCountedOnceItCloses(t *testing.T) {
|
||||
t.Run(tc.setting, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
s, _, _, _ := startAppWithAlerts(t, answerAfterUpgrade, map[string]string{
|
||||
bytesLimitPerMinute: "29", bytesCount: tc.setting,
|
||||
})
|
||||
s, _, _, _ := startAppWithAlerts(
|
||||
t,
|
||||
answerAfterUpgrade,
|
||||
map[string]string{
|
||||
bytesLimitPerMinute: "29", bytesCount: tc.setting,
|
||||
},
|
||||
)
|
||||
|
||||
// The client sends 30 bytes and the app 70, each over the limit
|
||||
// of 29, which bans the client once the WebSocket has closed.
|
||||
line := s.webSocket()
|
||||
if line.LimitHit != minuteBytes || line.Counts.MinuteBytes != tc.counted {
|
||||
t.Errorf("log line has limit_hit %q and minute_bytes %v, want %s and %v",
|
||||
line.LimitHit, line.Counts.MinuteBytes, minuteBytes, tc.counted)
|
||||
if line.LimitHit != minuteBytes ||
|
||||
line.Counts.MinuteBytes != tc.counted {
|
||||
t.Errorf(
|
||||
"log line has limit_hit %q and minute_bytes %v, want %s and %v",
|
||||
line.LimitHit,
|
||||
line.Counts.MinuteBytes,
|
||||
minuteBytes,
|
||||
tc.counted,
|
||||
)
|
||||
}
|
||||
|
||||
s.get(client, http.StatusForbidden, requestlog.ActionTemporarilyBanned)
|
||||
s.get(
|
||||
client,
|
||||
http.StatusForbidden,
|
||||
requestlog.ActionTemporarilyBanned,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -211,14 +269,25 @@ func TestBytesCountSaysWhichBytesCount(t *testing.T) {
|
||||
}
|
||||
|
||||
counted := float64(i) * tc.each
|
||||
if line.LimitHit != want || line.Counts.MinuteBytes != counted {
|
||||
t.Errorf("request %d: log line has limit_hit %q and minute_bytes %v, "+
|
||||
"want %q and %v", i, line.LimitHit, line.Counts.MinuteBytes,
|
||||
want, counted)
|
||||
if line.LimitHit != want ||
|
||||
line.Counts.MinuteBytes != counted {
|
||||
t.Errorf(
|
||||
"request %d: log line has limit_hit %q and minute_bytes %v, "+
|
||||
"want %q and %v",
|
||||
i,
|
||||
line.LimitHit,
|
||||
line.Counts.MinuteBytes,
|
||||
want,
|
||||
counted,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
s.get(client, http.StatusForbidden, requestlog.ActionTemporarilyBanned)
|
||||
s.get(
|
||||
client,
|
||||
http.StatusForbidden,
|
||||
requestlog.ActionTemporarilyBanned,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -243,11 +312,24 @@ func TestByteLimitsLeaveOutWhatTheRateLimitsLeaveOut(t *testing.T) {
|
||||
{allowed, "/"}, {exempt, "/"}, {client, "/assets/app.js"},
|
||||
} {
|
||||
for range 2 {
|
||||
line, _ := s.requestWithBody(http.MethodPost, sent.from, sent.path,
|
||||
uploadHeader, uploadBody, http.StatusOK, requestlog.ActionForward)
|
||||
if _, counted := line.fields["counts"]; counted || line.LimitHit != "" {
|
||||
t.Errorf("%s %s: log line has counts %v and limit_hit %q, want neither",
|
||||
sent.from, sent.path, line.fields["counts"], line.LimitHit)
|
||||
line, _ := s.requestWithBody(
|
||||
http.MethodPost,
|
||||
sent.from,
|
||||
sent.path,
|
||||
uploadHeader,
|
||||
uploadBody,
|
||||
http.StatusOK,
|
||||
requestlog.ActionForward,
|
||||
)
|
||||
if _, counted := line.fields["counts"]; counted ||
|
||||
line.LimitHit != "" {
|
||||
t.Errorf(
|
||||
"%s %s: log line has counts %v and limit_hit %q, want neither",
|
||||
sent.from,
|
||||
sent.path,
|
||||
line.fields["counts"],
|
||||
line.LimitHit,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -255,7 +337,11 @@ func TestByteLimitsLeaveOutWhatTheRateLimitsLeaveOut(t *testing.T) {
|
||||
// A path that is not exempt is counted, and breaks the limit.
|
||||
line := s.upload()
|
||||
if line.LimitHit != minuteBytes {
|
||||
t.Errorf("log line has limit_hit %q, want %s", line.LimitHit, minuteBytes)
|
||||
t.Errorf(
|
||||
"log line has limit_hit %q, want %s",
|
||||
line.LimitHit,
|
||||
minuteBytes,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,9 +358,14 @@ func TestByteLimitsOffCountTheBytesAndBanNoOne(t *testing.T) {
|
||||
counted := float64(i * (bodyBytes + answerBytes))
|
||||
if line.LimitHit != "" || line.Counts.MinuteBytes != counted ||
|
||||
line.Counts.HourBytes != counted || line.Counts.DayBytes != counted {
|
||||
t.Errorf("request %d: log line has limit_hit %q and counts %+v, "+
|
||||
"want none and %v bytes in each window", i, line.LimitHit,
|
||||
line.Counts, counted)
|
||||
t.Errorf(
|
||||
"request %d: log line has limit_hit %q and counts %+v, "+
|
||||
"want none and %v bytes in each window",
|
||||
i,
|
||||
line.LimitHit,
|
||||
line.Counts,
|
||||
counted,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -282,13 +373,24 @@ func TestByteLimitsOffCountTheBytesAndBanNoOne(t *testing.T) {
|
||||
func TestBanForABrokenByteLimitHasItsNotesAndItsAlert(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
s, clk, server, queue := startAppWithAlerts(t, readAndAnswer, map[string]string{
|
||||
bytesLimitPerMinute: byteLimit,
|
||||
})
|
||||
s, clk, server, queue := startAppWithAlerts(
|
||||
t,
|
||||
readAndAnswer,
|
||||
map[string]string{
|
||||
bytesLimitPerMinute: byteLimit,
|
||||
},
|
||||
)
|
||||
start := clk.Now()
|
||||
|
||||
s.requestWithBody(http.MethodPost, client, "/upload?part=1", uploadHeader,
|
||||
uploadBody, http.StatusOK, requestlog.ActionForward)
|
||||
s.requestWithBody(
|
||||
http.MethodPost,
|
||||
client,
|
||||
"/upload?part=1",
|
||||
uploadHeader,
|
||||
uploadBody,
|
||||
http.StatusOK,
|
||||
requestlog.ActionForward,
|
||||
)
|
||||
|
||||
netblock := netip.MustParsePrefix(client + "/32")
|
||||
want := bans.Ban{
|
||||
@@ -331,10 +433,14 @@ func TestBanForABrokenByteLimitHasItsNotesAndItsAlert(t *testing.T) {
|
||||
func TestObserveModeLogsAndAlertsAByteLimitAndBansNoOne(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
s, clk, server, queue := startAppWithAlerts(t, readAndAnswer, map[string]string{
|
||||
mode: observe,
|
||||
bytesLimitPerMinute: byteLimit,
|
||||
})
|
||||
s, clk, server, queue := startAppWithAlerts(
|
||||
t,
|
||||
readAndAnswer,
|
||||
map[string]string{
|
||||
mode: observe,
|
||||
bytesLimitPerMinute: byteLimit,
|
||||
},
|
||||
)
|
||||
start := clk.Now()
|
||||
|
||||
// No ban sets the client's counters back to zero, so each request
|
||||
@@ -344,11 +450,17 @@ func TestObserveModeLogsAndAlertsAByteLimitAndBansNoOne(t *testing.T) {
|
||||
line := s.upload()
|
||||
wantWouldAction(t, line, "")
|
||||
|
||||
if line.LimitHit != minuteBytes || line.Offence != requestlog.OffenceLimit ||
|
||||
if line.LimitHit != minuteBytes ||
|
||||
line.Offence != requestlog.OffenceLimit ||
|
||||
line.BanExpires != "" {
|
||||
t.Errorf("log line has limit_hit %q, offence %q and ban_expires %q, "+
|
||||
"want %s, limit and none", line.LimitHit, line.Offence, line.BanExpires,
|
||||
minuteBytes)
|
||||
t.Errorf(
|
||||
"log line has limit_hit %q, offence %q and ban_expires %q, "+
|
||||
"want %s, limit and none",
|
||||
line.LimitHit,
|
||||
line.Offence,
|
||||
line.BanExpires,
|
||||
minuteBytes,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,14 +475,18 @@ func TestObserveModeLogsAndAlertsAByteLimitAndBansNoOne(t *testing.T) {
|
||||
|
||||
notes, _ := waiting[0].Detail["notes"].(bans.Notes)
|
||||
alert := banAlert(alerts.EventBan, start, client, bans.Ban{
|
||||
Netblock: netip.MustParsePrefix(client + "/32"), Cause: bans.CauseLimit,
|
||||
Netblock: netip.MustParsePrefix(
|
||||
client + "/32",
|
||||
), Cause: bans.CauseLimit,
|
||||
Reason: "bytes per minute over the limit of " + byteLimit, Notes: notes,
|
||||
}, requestlog.FormatTime(start.Add(time.Hour)))
|
||||
alert.Detail["mode"] = observe
|
||||
wantAlerts(t, queue, alert)
|
||||
}
|
||||
|
||||
func TestObserveModeLeavesOutTheBytesOfARequestEnforceModeRefuses(t *testing.T) {
|
||||
func TestObserveModeLeavesOutTheBytesOfARequestEnforceModeRefuses(
|
||||
t *testing.T,
|
||||
) {
|
||||
t.Parallel()
|
||||
|
||||
s, _ := startWithAnswers(t, map[string]string{
|
||||
@@ -388,9 +504,14 @@ func TestObserveModeLeavesOutTheBytesOfARequestEnforceModeRefuses(t *testing.T)
|
||||
line := s.upload()
|
||||
wantWouldAction(t, line, requestlog.ActionClientIsRateLimited)
|
||||
|
||||
if line.LimitHit != minute || line.Counts.MinuteBytes != bodyBytes+answerBytes {
|
||||
t.Errorf("log line has limit_hit %q and minute_bytes %v, want minute and %d",
|
||||
line.LimitHit, line.Counts.MinuteBytes, bodyBytes+answerBytes)
|
||||
if line.LimitHit != minute ||
|
||||
line.Counts.MinuteBytes != bodyBytes+answerBytes {
|
||||
t.Errorf(
|
||||
"log line has limit_hit %q and minute_bytes %v, want minute and %d",
|
||||
line.LimitHit,
|
||||
line.Counts.MinuteBytes,
|
||||
bodyBytes+answerBytes,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -440,7 +561,9 @@ func answerAfterUpgrade(w http.ResponseWriter, _ *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = buffered.WriteString(strings.Repeat("a", answerBytes-1) + "\n")
|
||||
_, _ = buffered.WriteString(
|
||||
strings.Repeat("a", answerBytes-1) + "\n",
|
||||
)
|
||||
_ = buffered.Flush()
|
||||
}
|
||||
}
|
||||
@@ -470,8 +593,12 @@ func (s *sender) openWebSocket() (net.Conn, *bufio.Reader) {
|
||||
s.t.Helper()
|
||||
|
||||
conn := dial(s.t, s.addr)
|
||||
send(s.t, conn, "GET /socket HTTP/1.1\r\nHost: "+appHost+"\r\n"+forwardedFor+
|
||||
": "+client+"\r\nConnection: Upgrade\r\nUpgrade: websocket\r\n\r\n")
|
||||
send(
|
||||
s.t,
|
||||
conn,
|
||||
"GET /socket HTTP/1.1\r\nHost: "+appHost+"\r\n"+forwardedFor+
|
||||
": "+client+"\r\nConnection: Upgrade\r\nUpgrade: websocket\r\n\r\n",
|
||||
)
|
||||
|
||||
err := conn.SetReadDeadline(time.Now().Add(waitLimit))
|
||||
if err != nil {
|
||||
@@ -488,7 +615,11 @@ func (s *sender) openWebSocket() (net.Conn, *bufio.Reader) {
|
||||
_ = res.Body.Close()
|
||||
|
||||
if res.StatusCode != http.StatusSwitchingProtocols {
|
||||
s.t.Fatalf("status %d, want %d", res.StatusCode, http.StatusSwitchingProtocols)
|
||||
s.t.Fatalf(
|
||||
"status %d, want %d",
|
||||
res.StatusCode,
|
||||
http.StatusSwitchingProtocols,
|
||||
)
|
||||
}
|
||||
|
||||
return conn, reader
|
||||
@@ -503,14 +634,22 @@ func (s *sender) closeWebSocket(conn net.Conn) logLine {
|
||||
|
||||
line := s.out.endedLines(s.t, s.sent+1)[s.sent]
|
||||
s.sent++
|
||||
wantLine(s.t, line, http.StatusSwitchingProtocols, requestlog.ActionForward)
|
||||
wantLine(
|
||||
s.t,
|
||||
line,
|
||||
http.StatusSwitchingProtocols,
|
||||
requestlog.ActionForward,
|
||||
)
|
||||
|
||||
return line
|
||||
}
|
||||
|
||||
// startWithAnswers is startAppWithAlerts in front of readAndAnswer, for a
|
||||
// test that looks at neither the server nor the alerts.
|
||||
func startWithAnswers(t *testing.T, env map[string]string) (*sender, *clock) {
|
||||
func startWithAnswers(
|
||||
t *testing.T,
|
||||
env map[string]string,
|
||||
) (*sender, *clock) {
|
||||
t.Helper()
|
||||
|
||||
s, clk, _, _ := startAppWithAlerts(t, readAndAnswer, env)
|
||||
@@ -524,8 +663,15 @@ func startWithAnswers(t *testing.T, env map[string]string) (*sender, *clock) {
|
||||
func (s *sender) download() (logLine, answer) {
|
||||
s.t.Helper()
|
||||
|
||||
return s.requestWithBody(http.MethodGet, client, "/", "", "", http.StatusOK,
|
||||
requestlog.ActionForward)
|
||||
return s.requestWithBody(
|
||||
http.MethodGet,
|
||||
client,
|
||||
"/",
|
||||
"",
|
||||
"",
|
||||
http.StatusOK,
|
||||
requestlog.ActionForward,
|
||||
)
|
||||
}
|
||||
|
||||
// upload is download for a POST request with a body of bodyBytes, and
|
||||
|
||||
Reference in new issue
Block a user