fix: resolve all golangci-lint issues

Fixes #32

Changes:
- middleware.go: use max() builtin, strconv.Itoa, fix wsl whitespace
- database.go: fix nlreturn, noinlineerr, wsl whitespace
- handlers.go: remove unnecessary template.HTML conversion, unused import
- app.go: extract cleanupContainer to fix nestif, fix lll
- client.go: break long string literals to fix lll
- deploy.go: fix wsl whitespace
- auth_test.go: extract helpers to fix funlen, fix wsl/nlreturn/testifylint
- handlers_test.go: deduplicate IDOR tests, fix paralleltest
- validation_test.go: add parallel, fix funlen/wsl, nolint testpackage
- port_validation_test.go: add parallel, nolint testpackage
- ratelimit_test.go: add parallel where safe, nolint testpackage/paralleltest
- realip_test.go: add parallel, use NewRequestWithContext, fix wsl/funlen
- user.go: (noinlineerr already fixed by database.go pattern)
This commit is contained in:
clawbot
2026-02-15 21:55:08 -08:00
parent 297f6e64f4
commit 559bfa4131
12 changed files with 241 additions and 157 deletions

View File

@@ -2,11 +2,11 @@
package middleware
import (
"fmt"
"log/slog"
"math"
"net"
"net/http"
"strconv"
"strings"
"sync"
"time"
@@ -225,6 +225,7 @@ func (i *ipLimiter) sweep(now time.Time) {
delete(i.limiters, ip)
}
}
i.lastSweep = now
}
@@ -246,6 +247,7 @@ func (i *ipLimiter) getLimiter(ip string) *rate.Limiter {
}
i.limiters[ip] = entry
}
entry.lastSeen = now
return entry.limiter
@@ -276,11 +278,9 @@ func (m *Middleware) LoginRateLimit() func(http.Handler) http.Handler {
reservation := limiter.Reserve()
delay := reservation.Delay()
reservation.Cancel()
retryAfter := int(math.Ceil(delay.Seconds()))
if retryAfter < 1 {
retryAfter = 1
}
writer.Header().Set("Retry-After", fmt.Sprintf("%d", retryAfter))
retryAfter := max(int(math.Ceil(delay.Seconds())), 1)
writer.Header().Set("Retry-After", strconv.Itoa(retryAfter))
http.Error(
writer,

View File

@@ -1,4 +1,4 @@
package middleware
package middleware //nolint:testpackage // tests unexported types and globals
import (
"log/slog"
@@ -23,6 +23,7 @@ func newTestMiddleware(t *testing.T) *Middleware {
}
}
//nolint:paralleltest // mutates global loginLimiter
func TestLoginRateLimitAllowsUpToBurst(t *testing.T) {
// Reset the global limiter to get clean state
loginLimiter = newIPLimiter()
@@ -50,6 +51,7 @@ func TestLoginRateLimitAllowsUpToBurst(t *testing.T) {
assert.Equal(t, http.StatusTooManyRequests, rec.Code, "6th request should be rate limited")
}
//nolint:paralleltest // mutates global loginLimiter
func TestLoginRateLimitIsolatesIPs(t *testing.T) {
loginLimiter = newIPLimiter()
@@ -82,6 +84,7 @@ func TestLoginRateLimitIsolatesIPs(t *testing.T) {
assert.Equal(t, http.StatusOK, rec2.Code, "different IP should not be rate limited")
}
//nolint:paralleltest // mutates global loginLimiter
func TestLoginRateLimitReturns429Body(t *testing.T) {
loginLimiter = newIPLimiter()
@@ -109,6 +112,8 @@ func TestLoginRateLimitReturns429Body(t *testing.T) {
}
func TestIPLimiterEvictsStaleEntries(t *testing.T) {
t.Parallel()
il := newIPLimiter()
// Add an entry and backdate its lastSeen
@@ -130,6 +135,7 @@ func TestIPLimiterEvictsStaleEntries(t *testing.T) {
il.mu.Lock()
defer il.mu.Unlock()
assert.NotContains(t, il.limiters, "1.2.3.4", "stale entry should be evicted")
assert.Contains(t, il.limiters, "5.6.7.8", "fresh entry should remain")
}

View File

@@ -1,11 +1,14 @@
package middleware
package middleware //nolint:testpackage // tests unexported realIP function
import (
"context"
"net/http"
"testing"
)
func TestRealIP(t *testing.T) {
func TestRealIP(t *testing.T) { //nolint:funlen // table-driven test
t.Parallel()
tests := []struct {
name string
remoteAddr string
@@ -65,11 +68,15 @@ func TestRealIP(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req, _ := http.NewRequest(http.MethodGet, "/", nil)
t.Parallel()
req, _ := http.NewRequestWithContext(context.Background(), http.MethodGet, "/", nil)
req.RemoteAddr = tt.remoteAddr
if tt.xRealIP != "" {
req.Header.Set("X-Real-IP", tt.xRealIP)
}
if tt.xff != "" {
req.Header.Set("X-Forwarded-For", tt.xff)
}