mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-09-05 01:47:15 +00:00
Test-quality audit: fix 2 prod bugs, strengthen weak tests, add mutation/fuzz/CI tooling (#5345)
* test(audit): add gremlins/rapid/coverage tooling + AUDIT.md scaffold * test(audit): hygiene sweep (race-clean except logger global; Finding #2) + smell inventory * test(audit): cover untested error/edge branches (TLS proxy+pin, migration tag cleanup=Finding #1) * test(audit): strengthen internal/sub link tests (dedup key, TLS/Reality mapping, clash well-formedness) * test(audit): property (rapid) + fuzz tests for joinHostPort/userinfo/pin/ParseLink * test(audit): tighten frontend subSortIndex rejection assertions + wire coverage * ci(audit): add shuffle gate + non-blocking race job (Finding #2) + fuzz-smoke; document mutation policy * chore(audit): gitignore frontend coverage output * test(audit): exhaustive whole-repo pass — strengthen 5 weak/fake tests (netproxy, CSP, modal per-protocol loops, schema coercions) * docs(contributing): add Testing section (conventions, race/shuffle, fuzz, mutation policy); drop AUDIT.md ledger * fix(logger,migration): guard logBuffer with mutex; execute legacy tag cleanup (tx.Exec); make CI race gate blocking * ci(mutation): add nightly scoped gremlins workflow (informational artifacts) * test(audit): strengthen runtime tests — baseURL scheme/port bounds, isNonEmptySlice, trafficReset * test(audit): strengthen clash tests — reality field mapping + tcp-header validation * test(audit): runtime — egress-proxy + content-type tests; drop redundant bp=='' branch * test(audit): strengthen link parser/helper tests (defaultPort, splitComma, base64, canonicalQuery, tls/reality/transport mapping) * test(audit): strengthen sub/xray/common/netsafe/mtproto/config/middleware tests (kill surviving mutants) * test(audit): raise timeout on protocol-iteration modal tests (heavy re-renders, slow on CI) * fix(logger): GetLogs returns at most c entries (off-by-one fix; addresses PR review) * perf(logger): snapshot logBuffer under lock so GetLogs doesn't block logging; clarify fuzz-seed docs (addresses PR review)
This commit is contained in:
@@ -116,8 +116,67 @@ func TestHTTPClientForNodeVerifyShared(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHTTPClientForNodePinInvalid(t *testing.T) {
|
||||
if _, err := HTTPClientForNode(&model.Node{Scheme: "https", TlsVerifyMode: "pin", PinnedCertSha256: "not-a-pin"}, ""); err == nil {
|
||||
t.Fatal("expected error for invalid pin")
|
||||
// pin mode must fail closed, and with a specific error per cause — not merely
|
||||
// "some error" (which a bug anywhere in the build path would also satisfy).
|
||||
cases := []struct {
|
||||
name string
|
||||
pin string
|
||||
wantErr string
|
||||
}{
|
||||
{"garbage pin", "not-a-pin", "must be a SHA-256 hash"},
|
||||
{"empty pin", "", "certificate pin is empty"},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
_, err := HTTPClientForNode(&model.Node{Scheme: "https", TlsVerifyMode: "pin", PinnedCertSha256: c.pin}, "")
|
||||
if err == nil {
|
||||
t.Fatalf("expected error for pin %q", c.pin)
|
||||
}
|
||||
if !strings.Contains(err.Error(), c.wantErr) {
|
||||
t.Fatalf("error = %q, want it to contain %q", err.Error(), c.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestHTTPClientForNode_ProxyPinPreservesPinEnforcement covers the proxy+pin branch
|
||||
// (tls_client.go:43-52): when a node uses a proxy AND pin mode, the proxy client's
|
||||
// transport must carry the pinning tls.Config (the `transport.TLSClientConfig = tlsCfg`
|
||||
// line). Dropping it would silently disable certificate pinning whenever a proxy is set.
|
||||
func TestHTTPClientForNode_ProxyPinPreservesPinEnforcement(t *testing.T) {
|
||||
pin := base64.StdEncoding.EncodeToString(make([]byte, sha256.Size))
|
||||
n := &model.Node{Scheme: "https", TlsVerifyMode: "pin", PinnedCertSha256: pin}
|
||||
|
||||
c, err := HTTPClientForNode(n, "socks5://127.0.0.1:1080")
|
||||
if err != nil {
|
||||
t.Fatalf("HTTPClientForNode: %v", err)
|
||||
}
|
||||
if c == defaultNodeHTTPClient {
|
||||
t.Fatal("proxy client must not be the shared default client")
|
||||
}
|
||||
tr, ok := c.Transport.(*http.Transport)
|
||||
if !ok {
|
||||
t.Fatalf("transport is %T, want *http.Transport", c.Transport)
|
||||
}
|
||||
if tr.TLSClientConfig == nil || tr.TLSClientConfig.VerifyConnection == nil {
|
||||
t.Fatal("pin mode over a proxy must install a pinning tls.Config (VerifyConnection); pin enforcement was dropped")
|
||||
}
|
||||
}
|
||||
|
||||
// TestHTTPClientForNode_ProxyVerifyNoPin covers the proxy+verify branch
|
||||
// (tls_client.go:40-42): verify mode over a proxy returns the proxy client as-is,
|
||||
// using system-CA verification and NOT a pin VerifyConnection.
|
||||
func TestHTTPClientForNode_ProxyVerifyNoPin(t *testing.T) {
|
||||
n := &model.Node{Scheme: "https", TlsVerifyMode: "verify"}
|
||||
c, err := HTTPClientForNode(n, "socks5://127.0.0.1:1080")
|
||||
if err != nil {
|
||||
t.Fatalf("HTTPClientForNode: %v", err)
|
||||
}
|
||||
if c == defaultNodeHTTPClient {
|
||||
t.Fatal("proxy client must not be the shared default client")
|
||||
}
|
||||
if tr, ok := c.Transport.(*http.Transport); ok && tr.TLSClientConfig != nil && tr.TLSClientConfig.VerifyConnection != nil {
|
||||
t.Fatal("verify mode must not install a pin VerifyConnection")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user