fix(sub): default VLESS client encryption to none when the inbound has none stored (#6781)

VLESS inbounds whose settings carry no "encryption" key (rows saved by
panels before the field existed, or inbounds added over the API with only
"decryption":"none", as the API docs example does) produced a JSON
subscription outbound with "encryption": "". Xray refuses to load that
config ("VLESS users: please add/set \"encryption\":\"none\" for every
user"), so every VLESS entry of the JSON subscription was unusable. The
share link left the parameter out instead.

Treat a missing or empty value as "none" for both outputs, matching the
frontend inbound schema default and the docs link generator.
This commit is contained in:
Jay Hemnani
2026-10-12 14:33:47 +05:30
committed by GitHub
parent f55938ba0a
commit d43e0c7ead
3 changed files with 44 additions and 9 deletions
+33
View File
@@ -2,6 +2,7 @@ package sub
import (
"encoding/json"
"net/url"
"reflect"
"testing"
@@ -231,6 +232,38 @@ func TestSubJsonServiceVlessFlattened(t *testing.T) {
}
}
// VLESS inbounds stored without settings.encryption (pre-2.6.8 rows, API adds)
// must still give clients "none": xray refuses a VLESS outbound with "".
func TestSubVlessEncryptionDefaultsToNone(t *testing.T) {
cases := []struct {
name string
settings string
want string
}{
{"missing", `{"clients":[{"id":"uuid-1","email":"user"}],"decryption":"none"}`, "none"},
{"empty", `{"clients":[{"id":"uuid-1","email":"user"}],"decryption":"none","encryption":""}`, "none"},
{"vlessenc kept", `{"clients":[{"id":"uuid-1","email":"user"}],"decryption":"mlkem768x25519plus.native.600s.d","encryption":"mlkem768x25519plus.native.0rtt.e"}`, "mlkem768x25519plus.native.0rtt.e"},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
inbound := &model.Inbound{Listen: "1.2.3.4", Port: 443, Protocol: model.VLESS, Settings: tc.settings, StreamSettings: `{"network":"tcp","security":"none"}`}
settings := outboundSettings(t, NewSubJsonService("", "", "", "", nil).genVless(&SubService{}, inbound, nil, model.Client{ID: "uuid-1"}, ""))
if settings["encryption"] != tc.want {
t.Fatalf("json outbound encryption = %#v, want %q", settings["encryption"], tc.want)
}
link, err := url.Parse((&SubService{}).genVlessLink(inbound, "user"))
if err != nil {
t.Fatalf("parse link: %v", err)
}
if got := link.Query().Get("encryption"); got != tc.want {
t.Fatalf("share link encryption = %q, want %q (%s)", got, tc.want, link)
}
})
}
}
func TestSubJsonServiceVlessFlowSuppressedByDisableFlow(t *testing.T) {
inbound := &model.Inbound{Listen: "1.2.3.4", Port: 443, Protocol: model.VLESS, Settings: `{"encryption":"none"}`, DisableFlow: true}
client := model.Client{ID: "uuid-1", Flow: "xtls-rprx-vision"}