From 00aaef386fdc76f5566e83fdccfbe113ce318b1e Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Sun, 11 Oct 2026 10:56:47 +0200 Subject: [PATCH] fix(inbounds): accept masque in the inbound protocol validator The inbound add and update handlers bind the body into model.Inbound and validate Protocol against a oneof list that predates MASQUE, so every save of a MASQUE inbound from the panel or the API failed validation even though the rest of the stack (b04039c2) handles the protocol. Add masque to the list and regenerate the schemas and OpenAPI document that mirror it. --- docs/public/openapi.json | 3 ++- frontend/public/openapi.json | 3 ++- frontend/src/generated/schemas.ts | 3 ++- frontend/src/generated/zod.ts | 2 +- internal/database/model/model.go | 2 +- .../web/middleware/validate_protocol_test.go | 18 ++++++++++++++++++ 6 files changed, 26 insertions(+), 5 deletions(-) create mode 100644 internal/web/middleware/validate_protocol_test.go diff --git a/docs/public/openapi.json b/docs/public/openapi.json index 749ec2788..570f87ec3 100644 --- a/docs/public/openapi.json +++ b/docs/public/openapi.json @@ -3115,7 +3115,8 @@ "tun", "mtproto", "amneziawg", - "tuic" + "tuic", + "masque" ], "example": "vless", "type": "string" diff --git a/frontend/public/openapi.json b/frontend/public/openapi.json index 749ec2788..570f87ec3 100644 --- a/frontend/public/openapi.json +++ b/frontend/public/openapi.json @@ -3115,7 +3115,8 @@ "tun", "mtproto", "amneziawg", - "tuic" + "tuic", + "masque" ], "example": "vless", "type": "string" diff --git a/frontend/src/generated/schemas.ts b/frontend/src/generated/schemas.ts index 4c72b6ac7..6208b5d3f 100644 --- a/frontend/src/generated/schemas.ts +++ b/frontend/src/generated/schemas.ts @@ -3089,7 +3089,8 @@ export const SCHEMAS: Record = { "tun", "mtproto", "amneziawg", - "tuic" + "tuic", + "masque" ], "example": "vless", "type": "string" diff --git a/frontend/src/generated/zod.ts b/frontend/src/generated/zod.ts index b3fbcd047..744b93c06 100644 --- a/frontend/src/generated/zod.ts +++ b/frontend/src/generated/zod.ts @@ -757,7 +757,7 @@ export const InboundSchema = z.object({ nodeId: z.number().int().nullable().optional(), originNodeGuid: z.string().optional(), port: z.number().int().min(0).max(65535), - protocol: z.enum(['vmess', 'vless', 'trojan', 'shadowsocks', 'wireguard', 'hysteria', 'http', 'mixed', 'tunnel', 'tun', 'mtproto', 'amneziawg', 'tuic']), + protocol: z.enum(['vmess', 'vless', 'trojan', 'shadowsocks', 'wireguard', 'hysteria', 'http', 'mixed', 'tunnel', 'tun', 'mtproto', 'amneziawg', 'tuic', 'masque']), remark: z.string(), settings: z.unknown(), shareAddr: z.string(), diff --git a/internal/database/model/model.go b/internal/database/model/model.go index 831f14803..18c61158f 100644 --- a/internal/database/model/model.go +++ b/internal/database/model/model.go @@ -65,7 +65,7 @@ type Inbound struct { // Xray configuration fields Listen string `json:"listen" form:"listen"` Port int `json:"port" form:"port" validate:"gte=0,lte=65535" example:"443"` - Protocol Protocol `json:"protocol" form:"protocol" validate:"required,oneof=vmess vless trojan shadowsocks wireguard hysteria http mixed tunnel tun mtproto amneziawg tuic" example:"vless"` + Protocol Protocol `json:"protocol" form:"protocol" validate:"required,oneof=vmess vless trojan shadowsocks wireguard hysteria http mixed tunnel tun mtproto amneziawg tuic masque" example:"vless"` Settings string `json:"settings" form:"settings"` StreamSettings string `json:"streamSettings" form:"streamSettings"` Tag string `json:"tag" form:"tag" gorm:"unique" example:"in-443-tcp"` diff --git a/internal/web/middleware/validate_protocol_test.go b/internal/web/middleware/validate_protocol_test.go new file mode 100644 index 000000000..be98a5301 --- /dev/null +++ b/internal/web/middleware/validate_protocol_test.go @@ -0,0 +1,18 @@ +package middleware + +import ( + "testing" + + "github.com/mhsanaei/3x-ui/v3/internal/database/model" +) + +// The inbound add/update handlers validate the body with this validator, so a protocol +// missing from Inbound.Protocol's oneof makes every save of that protocol fail. +func TestValidateAcceptsMasqueInboundProtocol(t *testing.T) { + if err := validate.StructPartial(model.Inbound{Protocol: model.MASQUE}, "Protocol"); err != nil { + t.Fatalf("validate rejected a masque inbound: %v", err) + } + if err := validate.StructPartial(model.Inbound{Protocol: "dokodemo"}, "Protocol"); err == nil { + t.Fatal("validate accepted an unknown protocol; the oneof no longer guards it") + } +}