check / check (push) Successful in 2m31s
RFC1918 ranges are the default trusted proxy set on an omitted key; an explicit list replaces the default; an explicit empty list trusts no one; unparseable values abort startup; forwarded headers honored only from trusted peers. Independent review passed: #127 (comment) model: claude-opus-4-8 (implementation and review); merged by claude-fable-5
42 lines
1.1 KiB
Go
42 lines
1.1 KiB
Go
package handlers
|
|
|
|
import (
|
|
"bytes"
|
|
"log/slog"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"strings"
|
|
"testing"
|
|
|
|
"sneak.berlin/go/pixa/internal/clientip"
|
|
"sneak.berlin/go/pixa/internal/config"
|
|
)
|
|
|
|
// TestFailedLoginLogsResolvedClientIP verifies the failed-login record
|
|
// carries the resolved client IP from the request context, not the raw
|
|
// proxy peer address.
|
|
func TestFailedLoginLogsResolvedClientIP(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var buf bytes.Buffer
|
|
|
|
h := &Handlers{
|
|
log: slog.New(slog.NewJSONHandler(&buf, nil)),
|
|
config: &config.Config{SigningKey: testSigningKey},
|
|
}
|
|
|
|
form := url.Values{loginKeyField: {"wrong-key"}}
|
|
req := httptest.NewRequestWithContext(
|
|
t.Context(), http.MethodPost, "/",
|
|
strings.NewReader(form.Encode()))
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
req = req.WithContext(clientip.WithClientIP(req.Context(), "203.0.113.7"))
|
|
|
|
h.handleLoginPost(httptest.NewRecorder(), req)
|
|
|
|
if !strings.Contains(buf.String(), `"remote_addr":"203.0.113.7"`) {
|
|
t.Errorf("failed-login log missing resolved client IP; got %q", buf.String())
|
|
}
|
|
}
|