Harden HTTP defaults and proxy header trust.

Add explicit server timeout/header limits, require trusted proxy CIDRs when honoring forwarded IP headers, and document single-instance storage expectations.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-31 21:09:00 -05:00
parent dbf112a5b7
commit dbb72da312
7 changed files with 246 additions and 27 deletions
+90
View File
@@ -22,6 +22,21 @@ func TestValidateAppliesDefaultsAndParsedFields(t *testing.T) {
if cfg.Limits.MaxUploadSizeBytes != 100*1024*1024 {
t.Fatalf("MaxUploadSizeBytes = %d, want %d", cfg.Limits.MaxUploadSizeBytes, int64(100*1024*1024))
}
if cfg.Server.ReadHeaderTimeoutDur != 5*time.Second {
t.Fatalf("ReadHeaderTimeoutDur = %s, want 5s", cfg.Server.ReadHeaderTimeoutDur)
}
if cfg.Server.ReadTimeoutDur != 30*time.Second {
t.Fatalf("ReadTimeoutDur = %s, want 30s", cfg.Server.ReadTimeoutDur)
}
if cfg.Server.WriteTimeoutDur != 30*time.Second {
t.Fatalf("WriteTimeoutDur = %s, want 30s", cfg.Server.WriteTimeoutDur)
}
if cfg.Server.IdleTimeoutDur != 120*time.Second {
t.Fatalf("IdleTimeoutDur = %s, want 120s", cfg.Server.IdleTimeoutDur)
}
if cfg.Server.MaxHeaderBytes != 1<<20 {
t.Fatalf("MaxHeaderBytes = %d, want %d", cfg.Server.MaxHeaderBytes, 1<<20)
}
if cfg.Limits.DefaultTTLDuration != time.Hour {
t.Fatalf("DefaultTTLDuration = %s, want 1h", cfg.Limits.DefaultTTLDuration)
}
@@ -51,6 +66,11 @@ func TestLoadParsesConfiguredFields(t *testing.T) {
content := `
server:
listen_addr: "127.0.0.1:9090"
read_header_timeout: "4s"
read_timeout: "25s"
write_timeout: "35s"
idle_timeout: "90s"
max_header_bytes: 2097152
limits:
max_upload_size: "2MiB"
default_ttl: "2h"
@@ -64,6 +84,8 @@ security:
- "127.0.0.1"
- "10.0.0.0/8"
trust_proxy_headers: true
trusted_proxy_ips:
- "10.0.0.0/8"
rate_limit:
enabled: true
requests_per_minute: 12
@@ -81,6 +103,21 @@ security:
if cfg.Server.ListenAddr != "127.0.0.1:9090" {
t.Fatalf("ListenAddr = %q, want %q", cfg.Server.ListenAddr, "127.0.0.1:9090")
}
if cfg.Server.ReadHeaderTimeoutDur != 4*time.Second {
t.Fatalf("ReadHeaderTimeoutDur = %s, want 4s", cfg.Server.ReadHeaderTimeoutDur)
}
if cfg.Server.ReadTimeoutDur != 25*time.Second {
t.Fatalf("ReadTimeoutDur = %s, want 25s", cfg.Server.ReadTimeoutDur)
}
if cfg.Server.WriteTimeoutDur != 35*time.Second {
t.Fatalf("WriteTimeoutDur = %s, want 35s", cfg.Server.WriteTimeoutDur)
}
if cfg.Server.IdleTimeoutDur != 90*time.Second {
t.Fatalf("IdleTimeoutDur = %s, want 90s", cfg.Server.IdleTimeoutDur)
}
if cfg.Server.MaxHeaderBytes != 2*1024*1024 {
t.Fatalf("MaxHeaderBytes = %d, want %d", cfg.Server.MaxHeaderBytes, 2*1024*1024)
}
if cfg.Limits.MaxUploadSizeBytes != 2*1024*1024 {
t.Fatalf("MaxUploadSizeBytes = %d, want %d", cfg.Limits.MaxUploadSizeBytes, int64(2*1024*1024))
}
@@ -105,6 +142,9 @@ security:
if len(cfg.Security.AllowedPrefixes) != 2 {
t.Fatalf("AllowedPrefixes len = %d, want 2", len(cfg.Security.AllowedPrefixes))
}
if len(cfg.Security.TrustedProxyPrefixes) != 1 {
t.Fatalf("TrustedProxyPrefixes len = %d, want 1", len(cfg.Security.TrustedProxyPrefixes))
}
}
func TestValidateRejectsInvalidValues(t *testing.T) {
@@ -129,6 +169,41 @@ func TestValidateRejectsInvalidValues(t *testing.T) {
},
wantErr: "limits.max_upload_size",
},
{
name: "invalid read header timeout",
mutate: func(cfg *Config) {
cfg.Server.ReadHeaderTimeout = "bogus"
},
wantErr: "server.read_header_timeout",
},
{
name: "invalid read timeout",
mutate: func(cfg *Config) {
cfg.Server.ReadTimeout = "bogus"
},
wantErr: "server.read_timeout",
},
{
name: "invalid write timeout",
mutate: func(cfg *Config) {
cfg.Server.WriteTimeout = "bogus"
},
wantErr: "server.write_timeout",
},
{
name: "invalid idle timeout",
mutate: func(cfg *Config) {
cfg.Server.IdleTimeout = "bogus"
},
wantErr: "server.idle_timeout",
},
{
name: "invalid max header bytes",
mutate: func(cfg *Config) {
cfg.Server.MaxHeaderBytes = 0
},
wantErr: "server.max_header_bytes",
},
{
name: "invalid ttl parse",
mutate: func(cfg *Config) {
@@ -164,6 +239,21 @@ func TestValidateRejectsInvalidValues(t *testing.T) {
},
wantErr: "security.allowed_ips",
},
{
name: "invalid trusted proxy entry",
mutate: func(cfg *Config) {
cfg.Security.TrustedProxyIPs = []string{"not-an-ip"}
},
wantErr: "security.trusted_proxy_ips",
},
{
name: "trust proxy headers requires trusted proxies",
mutate: func(cfg *Config) {
cfg.Security.TrustProxyHeaders = true
cfg.Security.TrustedProxyIPs = nil
},
wantErr: "security.trusted_proxy_ips is required",
},
{
name: "invalid rate limit burst",
mutate: func(cfg *Config) {