chore: baseline snapshot before moderation-strictness feature

Repo .git was damaged (refs/ missing, index truncated, object store near-empty).
Repaired via git init (rebuilt refs), stale index preserved as .git/index.corrupt-20260910.bak,
then full worktree committed as a rollback baseline.
This commit is contained in:
tamakyi
2026-09-10 10:56:27 +08:00
commit e3e59e7c69
296 changed files with 67458 additions and 0 deletions
+127
View File
@@ -0,0 +1,127 @@
package shared
import (
"net/http/httptest"
"testing"
"github.com/tamakyi/TamaBox/internal/conf"
)
func TestResolveClientIPCloudflareHeader(t *testing.T) {
oldTrustProxy := conf.Security.TrustProxy
defer func() { conf.Security.TrustProxy = oldTrustProxy }()
conf.Security.TrustProxy = true
r := httptest.NewRequest("GET", "https://example.com/", nil)
r.RemoteAddr = "192.0.2.10:443"
r.Header.Set("CF-Connecting-IP", "198.51.100.20")
r.Header.Set("X-Real-IP", "198.51.100.30")
if got := ResolveClientIP(r); got != "198.51.100.20" {
t.Fatalf("ResolveClientIP() = %q, want Cloudflare client IP", got)
}
}
func TestResolveClientIPIgnoresHeadersWithoutTrust(t *testing.T) {
oldTrustProxy := conf.Security.TrustProxy
defer func() { conf.Security.TrustProxy = oldTrustProxy }()
conf.Security.TrustProxy = false
r := httptest.NewRequest("GET", "https://example.com/", nil)
r.RemoteAddr = "192.0.2.10:443"
r.Header.Set("CF-Connecting-IP", "198.51.100.20")
r.Header.Set("EO-Client-IP", "198.51.100.99")
if got := ResolveClientIP(r); got != "192.0.2.10" {
t.Fatalf("ResolveClientIP() = %q, want remote address", got)
}
}
// TestResolveClientIPEdgeOneHeader 验证腾讯 EdgeOne 的权威客户端头 EO-Client-IP
// 在 TrustProxy=true 时被信任(双 CDN 部署:境内 EdgeOne 回源不带 CF-Connecting-IP)。
func TestResolveClientIPEdgeOneHeader(t *testing.T) {
oldTrustProxy := conf.Security.TrustProxy
defer func() { conf.Security.TrustProxy = oldTrustProxy }()
conf.Security.TrustProxy = true
r := httptest.NewRequest("GET", "https://example.com/", nil)
r.RemoteAddr = "203.0.113.44:443" // 模拟 EdgeOne 边缘回源
r.Header.Set("EO-Client-IP", "198.51.100.99")
r.Header.Set("X-Forwarded-For", "203.0.113.99")
if got := ResolveClientIP(r); got != "198.51.100.99" {
t.Fatalf("ResolveClientIP() = %q, want EdgeOne client IP", got)
}
}
// TestResolveClientIPPrefersCloudflareOverEdgeOne 锁定权威头的优先级:
// 两个头并存(配置遗漏剥离规则时的异常态)时以 CF-Connecting-IP 为准,行为可预期。
func TestResolveClientIPPrefersCloudflareOverEdgeOne(t *testing.T) {
oldTrustProxy := conf.Security.TrustProxy
defer func() { conf.Security.TrustProxy = oldTrustProxy }()
conf.Security.TrustProxy = true
r := httptest.NewRequest("GET", "https://example.com/", nil)
r.RemoteAddr = "203.0.113.44:443"
r.Header.Set("CF-Connecting-IP", "198.51.100.20")
r.Header.Set("EO-Client-IP", "198.51.100.99")
if got := ResolveClientIP(r); got != "198.51.100.20" {
t.Fatalf("ResolveClientIP() = %q, want Cloudflare client IP", got)
}
}
// TestResolveClientIPEdgeOneInvalidFallsBack 验证 EO-Client-IP 为非法值时
// 不 panic 且回退到 RemoteAddr(与 CF-Connecting-IP 的健壮性一致)。
func TestResolveClientIPEdgeOneInvalidFallsBack(t *testing.T) {
oldTrustProxy := conf.Security.TrustProxy
defer func() { conf.Security.TrustProxy = oldTrustProxy }()
conf.Security.TrustProxy = true
r := httptest.NewRequest("GET", "https://example.com/", nil)
r.RemoteAddr = "203.0.113.44:443"
r.Header.Set("EO-Client-IP", "not-an-ip")
if got := ResolveClientIP(r); got != "203.0.113.44" {
t.Fatalf("ResolveClientIP() = %q, want remote address fallback", got)
}
}
// TestLimitKey 是 P1-4 的回归测试:限流 / 自动封禁用的聚合键必须
// - IPv4:原样返回(每地址独立计数);
// - IPv6:掩码到 /64(防止单 /64 内轮换海量地址绕过阈值、并避免键无限膨胀);
// - 非法输入:原样返回,保持调用方行为可预期。
func TestLimitKey(t *testing.T) {
cases := []struct {
name string
in string
want string
}{
{"ipv4 passthrough", "203.0.113.10", "203.0.113.10"},
{"ipv4-mapped collapses to v4", "::ffff:203.0.113.10", "203.0.113.10"},
{"ipv6 aggregated to /64", "2001:db8:1:2:3:4:5:6", "2001:db8:1:2::/64"},
// LimitKey 假定输入已由 ResolveClientIP 归一化(已剥离端口)。若误传入带端口的
// 字符串,ParseIP 失败会原样透传 —— 这是防御性约定,需锁定以免行为漂移。
{"ipv4 with port passes through", "203.0.113.10:8080", "203.0.113.10:8080"},
{"ipv6 with port passes through", "[2001:db8:1:2:3:4:5:6]:443", "[2001:db8:1:2:3:4:5:6]:443"},
{"invalid input passthrough", "not-an-ip", "not-an-ip"},
{"empty passthrough", "", ""},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
if got := LimitKey(tc.in); got != tc.want {
t.Fatalf("LimitKey(%q) = %q, want %q", tc.in, got, tc.want)
}
})
}
}
// TestLimitKeyIPv6SamePrefixCollapses 验证:同一 /64 内任意两台主机应聚合到同一键,
// 不同 /64 必须落到不同键 —— 这是限速 / 封禁抗 IPv6 轮换绕过的核心不变量。
func TestLimitKeyIPv6SamePrefixCollapses(t *testing.T) {
a := LimitKey("2001:db8:1:2:3:4:5:6")
b := LimitKey("2001:db8:1:2:ff:fe::1") // 同一 /64,仅主机位不同
if a != b {
t.Fatalf("同一 /64 内地址应聚合到同一键,got %q vs %q", a, b)
}
c := LimitKey("2001:db8:1:3:3:4:5:6") // 不同 /64
if a == c {
t.Fatalf("不同 /64 应落到不同键,两者均为 %q", a)
}
}