trusted proxy, TOTP replay protection, new tests
- Trusted proxy config option for proxy-aware IP extraction used by rate limiting and audit logs; validates proxy IP before trusting X-Forwarded-For / X-Real-IP headers - TOTP replay protection via counter-based validation to reject reused codes within the same time step (±30s) - RateLimit middleware updated to extract client IP from proxy headers without IP spoofing risk - New tests for ClientIP proxy logic (spoofed headers, fallback) and extended rate-limit proxy coverage - HTMX error banner script integrated into web UI base - .gitignore updated for mciasdb build artifact Security: resolves CRIT-01 (TOTP replay attack) and DEF-03 (proxy-unaware rate limiting); gRPC TOTP enrollment aligned with REST via StorePendingTOTP Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -210,6 +210,40 @@ threads = 4
|
||||
}
|
||||
}
|
||||
|
||||
// TestTrustedProxyValidation verifies that trusted_proxy must be a valid IP.
|
||||
func TestTrustedProxyValidation(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
proxy string
|
||||
wantErr bool
|
||||
}{
|
||||
{"empty is valid (disabled)", "", false},
|
||||
{"valid IPv4", "127.0.0.1", false},
|
||||
{"valid IPv6 loopback", "::1", false},
|
||||
{"valid private IPv4", "10.0.0.1", false},
|
||||
{"hostname rejected", "proxy.example.com", true},
|
||||
{"CIDR rejected", "10.0.0.0/8", true},
|
||||
{"garbage rejected", "not-an-ip", true},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
cfg, _ := Load(writeTempConfig(t, validConfig()))
|
||||
if cfg == nil {
|
||||
t.Fatal("baseline config load failed")
|
||||
}
|
||||
cfg.Server.TrustedProxy = tc.proxy
|
||||
err := cfg.validate()
|
||||
if tc.wantErr && err == nil {
|
||||
t.Errorf("expected validation error for proxy=%q, got nil", tc.proxy)
|
||||
}
|
||||
if !tc.wantErr && err != nil {
|
||||
t.Errorf("unexpected error for proxy=%q: %v", tc.proxy, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDurationParsing(t *testing.T) {
|
||||
var d duration
|
||||
if err := d.UnmarshalText([]byte("1h30m")); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user