From 25a3afaff1ab23d272b976cdac919e2c9bd29ed4 Mon Sep 17 00:00:00 2001
From: CyberMind-FR
-} - -func suffixMatch(host string, pats []string) bool { - h := strings.ToLower(strings.TrimSpace(host)) - for _, p := range pats { - p = strings.ToLower(p) - if h == p || strings.HasSuffix(h, "."+p) { - return true - } - } - return false -} - -// action: "block" (204), "splice" (passthrough), or "mitm". -func (p Policy) action(host string) string { - if suffixMatch(host, p.SpliceHosts) { - return "splice" - } - if suffixMatch(host, p.AdHosts) { - return "block" - } - return "mitm" -} +// ── Pure handler logic ─────────────────────────────────────────────────────── +// +// The decision surface (Decide / action / registrable / splice helpers) lives +// in policy.go, ported from the Python addons and proven at parity by the +// cross-engine harness. The body-inject helper is kept here next to the wiring. // injectMarker inserts p.Inject before (else , else prepends). -func (p Policy) injectMarker(body []byte) []byte { +func (p *Policy) injectMarker(body []byte) []byte { if len(p.Inject) == 0 || bytes.Contains(body, p.Inject) { return body } @@ -201,7 +177,7 @@ func ja4ish(h *tls.ClientHelloInfo) string { type Proxy struct { ca *CA - pol Policy + pol *Policy jaSink func(string) // JA4 observations (logged; a sidecar in prod) } @@ -290,13 +266,17 @@ func main() { if err != nil { log.Fatalf("CA load: %v", err) } + // Load the BLOCK/SPLICE policy from the SAME on-disk config the Python + // addons read (defaults + env overrides). Missing files are tolerated + // (best-effort, like the addons): the engine then simply MITMs everything. + pol, err := LoadPolicy(PolicyOpts{}) + if err != nil { + log.Fatalf("policy load: %v", err) + } + pol.Inject = []byte("") px := &Proxy{ - ca: ca, - pol: Policy{ - AdHosts: []string{"doubleclick.net", "googlesyndication.com"}, - SpliceHosts: []string{"googlevideo.com", "fbcdn.net"}, - Inject: []byte(""), - }, + ca: ca, + pol: pol, jaSink: func(s string) { log.Printf("ja4 %s", s) }, } srv := &http.Server{Addr: *addr, Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/packages/secubox-toolbox-ng/cmd/sbxmitm/main_test.go b/packages/secubox-toolbox-ng/cmd/sbxmitm/main_test.go index 40b87f4f..d95f1208 100644 --- a/packages/secubox-toolbox-ng/cmd/sbxmitm/main_test.go +++ b/packages/secubox-toolbox-ng/cmd/sbxmitm/main_test.go @@ -72,24 +72,13 @@ func TestForgeChainsToCA(t *testing.T) { } } -func TestActionDecision(t *testing.T) { - p := Policy{AdHosts: []string{"doubleclick.net"}, SpliceHosts: []string{"googlevideo.com"}} - cases := map[string]string{ - "ads.doubleclick.net": "block", - "doubleclick.net": "block", - "r1.googlevideo.com": "splice", - "news.example.com": "mitm", - "notdoubleclick.net": "mitm", - } - for host, want := range cases { - if got := p.action(host); got != want { - t.Errorf("action(%q)=%q want %q", host, got, want) - } - } -} +// NOTE (#662 Phase 3): the old TestActionDecision drove the removed hardcoded +// Policy{AdHosts, SpliceHosts} fields. The decision surface now loads from +// disk (LoadPolicy) and mirrors the Python addons; coverage moved to +// TestParityDecide / TestPolicyActionVerbs in policy_test.go. func TestInjectMarker(t *testing.T) { - p := Policy{Inject: []byte("")} + p := &Policy{Inject: []byte("")} out := string(p.injectMarker([]byte("