Bump golangci-lint from v2.10.1 to v2.12.2 in the Dockerfile lint
stage (tag+digest pin) and script/bootstrap release-archive pins
(linux amd64/arm64 sha256s). Replace .golangci.yml with the canonical
v2-layout config so linter settings (lll 88, funlen 80/50, cyclop 15,
dupl 100) actually apply.
Fix all findings surfaced by the new linter and config:
- noctx: use httptest.NewRequestWithContext in all tests
- gosec G710/G703: route app redirects through a path-escaping
redirectToApp helper; annotate internal log path usage
- goconst: introduce shared constants for template/JSON keys and
repeated test literals
- lll: wrap lines to the 88-column limit
- dupl: extract shared helpers (generic findAllByAppID in models,
deleteAppResource in handlers, parsePush in webhook payloads,
table-driven/helper-based test dedup)
- nolintlint: drop nolint directives made obsolete by the new limits
Record the change in TODO.md; make check is green.
realIP() now parses RemoteAddr and checks if the source IP is in
RFC1918 (10/8, 172.16/12, 192.168/16), loopback (127/8), or IPv6
ULA/loopback ranges before trusting X-Real-IP or X-Forwarded-For
headers. Public source IPs have headers ignored (fail closed).
This prevents attackers from spoofing X-Forwarded-For to bypass
the login rate limiter.
Behind a reverse proxy like Traefik, RemoteAddr always contains the
proxy's IP. Add realIP() helper that checks X-Real-IP first, then the
first entry of X-Forwarded-For, falling back to RemoteAddr.
Update both LoginRateLimit and Logging middleware to use realIP().
Add comprehensive tests for the new function.
Fixes#12