From 8c63f7cc81f4dcdd9d4101cdf4b9860bca2a5898 Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Wed, 15 Jul 2026 03:17:33 +0200 Subject: [PATCH] fix(xray): keep source- and domains-scoped routing rules when an inbound is deleted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit removeInboundTagFromRules drops a routing rule whose inboundTag list becomes empty only if the rule has no other matcher, but routingMatcherKeys omitted xray-core's canonical source and domains keys. A rule scoped by source or domains (common in hand-authored or imported configs) therefore lost its whole body — including a security-relevant block — when its single listed inbound was deleted, instead of just having the tag trimmed. Recognize source and domains as live matchers. --- .../web/service/xray_setting_routing_sync.go | 4 +-- .../service/xray_setting_routing_sync_test.go | 33 +++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/internal/web/service/xray_setting_routing_sync.go b/internal/web/service/xray_setting_routing_sync.go index 363fcb33c..c230b6f49 100644 --- a/internal/web/service/xray_setting_routing_sync.go +++ b/internal/web/service/xray_setting_routing_sync.go @@ -5,8 +5,8 @@ import ( ) var routingMatcherKeys = []string{ - "domain", "ip", "port", "sourcePort", "localPort", "network", - "sourceIP", "localIP", "user", "vlessRoute", "protocol", "attrs", "process", + "domain", "domains", "ip", "port", "sourcePort", "localPort", "network", + "source", "sourceIP", "localIP", "user", "vlessRoute", "protocol", "attrs", "process", } func readInboundTags(raw any) []string { diff --git a/internal/web/service/xray_setting_routing_sync_test.go b/internal/web/service/xray_setting_routing_sync_test.go index c5ac2b28b..cf3e1b7ec 100644 --- a/internal/web/service/xray_setting_routing_sync_test.go +++ b/internal/web/service/xray_setting_routing_sync_test.go @@ -158,6 +158,39 @@ func TestRemoveInboundTagReferences_KeepsRuleWithOtherMatchers(t *testing.T) { } } +func TestRemoveInboundTagReferences_KeepsSourceScopedRule(t *testing.T) { + setupSettingTestDB(t) + seedXrayTemplate(t, `{ + "routing": { + "rules": [ + { + "type":"field", + "inboundTag":["in-443-tcp"], + "source":["10.0.0.0/8"], + "outboundTag":"blocked" + } + ] + } + }`) + + svc := &XraySettingService{} + if _, err := svc.RemoveInboundTagReferences("in-443-tcp"); err != nil { + t.Fatalf("RemoveInboundTagReferences: %v", err) + } + + got, err := svc.GetXrayConfigTemplate() + if err != nil { + t.Fatalf("GetXrayConfigTemplate: %v", err) + } + rule := findRuleByOutbound(t, got, "blocked") + if _, ok := rule["inboundTag"]; ok { + t.Fatalf("inboundTag should be trimmed, rule = %#v", rule) + } + if src, _ := rule["source"].([]any); len(src) != 1 { + t.Fatalf("source-scoped rule was dropped instead of kept; rule = %#v", rule) + } +} + func TestRemoveInboundTagReferences_RemovesOneTagFromMultiInboundRule(t *testing.T) { setupSettingTestDB(t) seedXrayTemplate(t, `{