99 lines
3.6 KiB
Go
99 lines
3.6 KiB
Go
package service
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/mhsanaei/3x-ui/v3/internal/database/model"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/web/service/probe"
|
|
)
|
|
|
|
// Rider (B6): the anonymous public view must carry ipv4/ipv6 stack
|
|
// existence markers so the theme's IpStackBadges light up, without
|
|
// ever disclosing a real address. Existence = agent-reported address
|
|
// or master-observed (seen) source; pins do not count.
|
|
|
|
const (
|
|
stackReal4 = "203.0.113.77"
|
|
stackReal6 = "2001:db8:77::1"
|
|
stackSeen4 = "198.51.100.88"
|
|
stackSeen6 = "2001:db8:88::1"
|
|
stackPin4 = "192.0.2.99"
|
|
stackPin6 = "2001:db8:99::1"
|
|
)
|
|
|
|
func stackSource(agent model.AgentNode, meta probe.Meta) probeNodeSource {
|
|
return probeNodeSource{agent: agent, meta: meta, name: "stack-node"}
|
|
}
|
|
|
|
func TestProbeAnonViewStackMarkers(t *testing.T) {
|
|
// Agent-reported addresses light both markers.
|
|
src := stackSource(model.AgentNode{IPv4: stackReal4, IPv6: stackReal6}, probe.Meta{Public: true})
|
|
view := buildProbeNodeView(src, false)
|
|
if view["ipv4"] != "1" || view["ipv6"] != "1" {
|
|
t.Fatalf("reported stacks: ipv4=%v ipv6=%v, want both \"1\"", view["ipv4"], view["ipv6"])
|
|
}
|
|
|
|
// Seen-only (NAT node) lights the marker too.
|
|
src = stackSource(model.AgentNode{SeenIPv4: stackSeen4}, probe.Meta{Public: true})
|
|
view = buildProbeNodeView(src, false)
|
|
if view["ipv4"] != "1" {
|
|
t.Fatalf("seen-only v4: ipv4=%v, want \"1\"", view["ipv4"])
|
|
}
|
|
if view["ipv6"] != "" {
|
|
t.Fatalf("no v6 anywhere: ipv6=%v, want empty", view["ipv6"])
|
|
}
|
|
|
|
// No stack anywhere → both empty.
|
|
src = stackSource(model.AgentNode{}, probe.Meta{Public: true})
|
|
view = buildProbeNodeView(src, false)
|
|
if view["ipv4"] != "" || view["ipv6"] != "" {
|
|
t.Fatalf("no stacks: ipv4=%v ipv6=%v, want both empty", view["ipv4"], view["ipv6"])
|
|
}
|
|
|
|
// A pin alone is not a stack: markers stay empty.
|
|
src = stackSource(model.AgentNode{}, probe.Meta{Public: true, IPv4Pin: stackPin4, IPv6Pin: stackPin6})
|
|
view = buildProbeNodeView(src, false)
|
|
if view["ipv4"] != "" || view["ipv6"] != "" {
|
|
t.Fatalf("pin-only: ipv4=%v ipv6=%v, want both empty (pins do not count)", view["ipv4"], view["ipv6"])
|
|
}
|
|
}
|
|
|
|
func TestProbeAnonViewNeverLeaksAddresses(t *testing.T) {
|
|
src := stackSource(
|
|
model.AgentNode{IPv4: stackReal4, IPv6: stackReal6, SeenIPv4: stackSeen4, SeenIPv6: stackSeen6},
|
|
probe.Meta{Public: true, IPv4Pin: stackPin4, IPv6Pin: stackPin6},
|
|
)
|
|
view := buildProbeNodeView(src, false)
|
|
raw, err := json.Marshal(view)
|
|
if err != nil {
|
|
t.Fatalf("marshal anon view: %v", err)
|
|
}
|
|
for _, addr := range []string{stackReal4, stackReal6, stackSeen4, stackSeen6, stackPin4, stackPin6} {
|
|
if strings.Contains(string(raw), addr) {
|
|
t.Fatalf("anonymous view leaks address %s: %s", addr, raw)
|
|
}
|
|
}
|
|
// The admin-only address keys stay absent in the anonymous shape.
|
|
for _, key := range []string{"addresses", "ipv4_pin", "ipv6_pin", "ipv4_auto", "ipv6_auto", "hostname", "ip", "remark"} {
|
|
if _, ok := view[key]; ok {
|
|
t.Fatalf("anonymous view unexpectedly carries admin key %q", key)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestProbeFullViewAddressesUnchanged(t *testing.T) {
|
|
src := stackSource(
|
|
model.AgentNode{IPv4: stackReal4, IPv6: stackReal6, SeenIPv4: stackSeen4, SeenIPv6: stackSeen6},
|
|
probe.Meta{Public: true, IPv4Pin: stackPin4, IPv6Pin: stackPin6},
|
|
)
|
|
view := buildProbeNodeView(src, true)
|
|
if view["ipv4"] != stackReal4 || view["ipv6"] != stackReal6 {
|
|
t.Fatalf("full view addresses changed: ipv4=%v ipv6=%v", view["ipv4"], view["ipv6"])
|
|
}
|
|
if view["ipv4_pin"] != stackPin4 || view["ipv6_pin"] != stackPin6 {
|
|
t.Fatalf("full view pins changed: ipv4_pin=%v ipv6_pin=%v", view["ipv4_pin"], view["ipv6_pin"])
|
|
}
|
|
}
|