Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/MHSanaei/3x-ui.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMHSanaei <ho3ein.sanaei@gmail.com>2026-04-21 14:04:39 +0300
committerMHSanaei <ho3ein.sanaei@gmail.com>2026-04-21 14:04:39 +0300
commit53fb4fe8f9a7c744f3bb1482e0216f8eab4d8155 (patch)
tree886bcf5b8e643f14923dd4c71171a92d823fa8d6
parent8d512d55e59b4e24383d449325b77661b89741c3 (diff)
fix: prevent AddUser panic on nil flow for VLESS XHTTP clients
#4056
-rw-r--r--xray/api.go90
1 files changed, 79 insertions, 11 deletions
diff --git a/xray/api.go b/xray/api.go
index 52dbc14d..a887d666 100644
--- a/xray/api.go
+++ b/xray/api.go
@@ -37,6 +37,34 @@ type XrayAPI struct {
isConnected bool
}
+func getRequiredUserString(user map[string]any, key string) (string, error) {
+ value, ok := user[key]
+ if !ok || value == nil {
+ return "", fmt.Errorf("missing required user field %q", key)
+ }
+
+ strValue, ok := value.(string)
+ if !ok {
+ return "", fmt.Errorf("invalid type for user field %q: %T", key, value)
+ }
+
+ return strValue, nil
+}
+
+func getOptionalUserString(user map[string]any, key string) (string, error) {
+ value, ok := user[key]
+ if !ok || value == nil {
+ return "", nil
+ }
+
+ strValue, ok := value.(string)
+ if !ok {
+ return "", fmt.Errorf("invalid type for user field %q: %T", key, value)
+ }
+
+ return strValue, nil
+}
+
// Init connects to the Xray API server and initializes handler and stats service clients.
func (x *XrayAPI) Init(apiPort int) error {
if apiPort <= 0 || apiPort > math.MaxUint16 {
@@ -104,16 +132,36 @@ func (x *XrayAPI) DelInbound(tag string) error {
// AddUser adds a user to an inbound in the Xray core using the specified protocol and user data.
func (x *XrayAPI) AddUser(Protocol string, inboundTag string, user map[string]any) error {
+ userEmail, err := getRequiredUserString(user, "email")
+ if err != nil {
+ return err
+ }
+
var account *serial.TypedMessage
switch Protocol {
case "vmess":
+ userID, err := getRequiredUserString(user, "id")
+ if err != nil {
+ return err
+ }
+
account = serial.ToTypedMessage(&vmess.Account{
- Id: user["id"].(string),
+ Id: userID,
})
case "vless":
+ userID, err := getRequiredUserString(user, "id")
+ if err != nil {
+ return err
+ }
+
+ userFlow, err := getOptionalUserString(user, "flow")
+ if err != nil {
+ return err
+ }
+
vlessAccount := &vless.Account{
- Id: user["id"].(string),
- Flow: user["flow"].(string),
+ Id: userID,
+ Flow: userFlow,
}
// Add testseed if provided
if testseedVal, ok := user["testseed"]; ok {
@@ -139,12 +187,27 @@ func (x *XrayAPI) AddUser(Protocol string, inboundTag string, user map[string]an
}
account = serial.ToTypedMessage(vlessAccount)
case "trojan":
+ password, err := getRequiredUserString(user, "password")
+ if err != nil {
+ return err
+ }
+
account = serial.ToTypedMessage(&trojan.Account{
- Password: user["password"].(string),
+ Password: password,
})
case "shadowsocks":
+ cipher, err := getOptionalUserString(user, "cipher")
+ if err != nil {
+ return err
+ }
+
+ password, err := getRequiredUserString(user, "password")
+ if err != nil {
+ return err
+ }
+
var ssCipherType shadowsocks.CipherType
- switch user["cipher"].(string) {
+ switch cipher {
case "aes-128-gcm":
ssCipherType = shadowsocks.CipherType_AES_128_GCM
case "aes-256-gcm":
@@ -159,18 +222,23 @@ func (x *XrayAPI) AddUser(Protocol string, inboundTag string, user map[string]an
if ssCipherType != shadowsocks.CipherType_NONE {
account = serial.ToTypedMessage(&shadowsocks.Account{
- Password: user["password"].(string),
+ Password: password,
CipherType: ssCipherType,
})
} else {
account = serial.ToTypedMessage(&shadowsocks_2022.ServerConfig{
- Key: user["password"].(string),
- Email: user["email"].(string),
+ Key: password,
+ Email: userEmail,
})
}
case "hysteria":
+ auth, err := getRequiredUserString(user, "auth")
+ if err != nil {
+ return err
+ }
+
account = serial.ToTypedMessage(&hysteriaAccount.Account{
- Auth: user["auth"].(string),
+ Auth: auth,
})
default:
return nil
@@ -178,11 +246,11 @@ func (x *XrayAPI) AddUser(Protocol string, inboundTag string, user map[string]an
client := *x.HandlerServiceClient
- _, err := client.AlterInbound(context.Background(), &command.AlterInboundRequest{
+ _, err = client.AlterInbound(context.Background(), &command.AlterInboundRequest{
Tag: inboundTag,
Operation: serial.ToTypedMessage(&command.AddUserOperation{
User: &protocol.User{
- Email: user["email"].(string),
+ Email: userEmail,
Account: account,
},
}),