From fde66ba8209c994f092566e415ec33915b6952c8 Mon Sep 17 00:00:00 2001 From: H-TTTTT <36735327+H-TTTTT@users.noreply.github.com> Date: Tue, 21 Jul 2026 21:51:06 +0800 Subject: [PATCH] fix(sub): omit non-standard fm param from Hysteria2 URI (#6048) Hysteria2 subscription links included a v2rayN-specific fm= finalmask dump alongside standard obfs/obfs-password. Clients such as mihomo reject the unknown query param and fail to update the provider. Emit only the standard salamander fields in genHysteriaLink, matching the Clash generator. Fixes #5982 --- internal/sub/service.go | 7 +++---- internal/sub/service_test.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/internal/sub/service.go b/internal/sub/service.go index 6bc436601..e5c777b92 100644 --- a/internal/sub/service.go +++ b/internal/sub/service.go @@ -1025,11 +1025,10 @@ func (s *SubService) genHysteriaLink(inbound *model.Inbound, email string) strin } } - // salamander obfs (Hysteria2). The panel-side link generator already - // emits these; keep the subscription output in sync so a client has - // the obfs password to match the server. + // salamander obfs (Hysteria2). Emit only the standard URI fields; + // the non-standard fm= finalmask dump breaks mihomo and other + // Hysteria2 clients that reject unknown query params. if finalmask, ok := stream["finalmask"].(map[string]any); ok { - applyFinalMaskParams(finalmask, params) if udpMasks, ok := finalmask["udp"].([]any); ok { for _, m := range udpMasks { mask, _ := m.(map[string]any) diff --git a/internal/sub/service_test.go b/internal/sub/service_test.go index 1b8d926c7..32f9ebe87 100644 --- a/internal/sub/service_test.go +++ b/internal/sub/service_test.go @@ -1110,3 +1110,32 @@ func TestHysteriaHopPorts(t *testing.T) { }) } } + +func TestGenHysteriaLinkOmitsFinalMaskQueryParam(t *testing.T) { + stream := `{ + "security":"tls", + "tlsSettings":{"serverName":"hy.sni","alpn":["h3"],"settings":{"fingerprint":"chrome"}}, + "finalmask":{"udp":[{"type":"salamander","settings":{"password":"obfs-secret"}}]} + }` + in := &model.Inbound{ + Listen: "203.0.113.1", + Port: 443, + Protocol: model.Hysteria, + Remark: "hy2", + Settings: `{"version":2,"clients":[{"auth":"hyauth","email":"user"}]}`, + StreamSettings: stream, + } + got := (&SubService{}).genHysteriaLink(in, "user") + if got == "" { + t.Fatal("expected hysteria2 link") + } + if strings.Contains(got, "fm=") { + t.Fatalf("hysteria2 subscription URI must not include non-standard fm param: %s", got) + } + if !strings.Contains(got, "obfs=salamander") { + t.Fatalf("missing standard obfs=salamander: %s", got) + } + if !strings.Contains(got, "obfs-password=obfs-secret") { + t.Fatalf("missing standard obfs-password: %s", got) + } +}