From 302f5d9d515c1796a93ebe64c6c8eda39106ed43 Mon Sep 17 00:00:00 2001 From: dualfroz Date: Fri, 4 Sep 2026 09:31:42 +0200 Subject: [PATCH] fix(flag): honor full separator length when splitting map key=value pairs splitKeyVal sliced the value from index+1, skipping only one byte of the key/value separator. With a multi-character sep tag (e.g. sep:"=>") the remaining separator bytes leaked into the value. Slice from index+len(sep) so the whole separator is consumed. --- cli_test.go | 21 +++++++++++++++++++++ flag.go | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/cli_test.go b/cli_test.go index ab79c43..068208b 100644 --- a/cli_test.go +++ b/cli_test.go @@ -727,3 +727,24 @@ func TestDecoder(t *testing.T) { assert.Nil(t, flagSet.err) assert.Equal(t, v.D, customT{K1: "string", K2: 2}) } + +func TestMapMultiCharSep(t *testing.T) { + // A multi-character separator must not leak its tail into the value. + if k, v, _ := splitKeyVal("key=>val", "=>"); k != "key" || v != "val" { + t.Errorf(`splitKeyVal("key=>val", "=>") = (%q, %q), want ("key", "val")`, k, v) + } + + // Public API: a map flag configured with a multi-char sep. + type T struct { + Map map[string]string `cli:"m" sep:"=>"` + } + clr := color.Color{} + v := new(T) + flagSet := parseArgv([]string{"-mkey=>val"}, v, clr) + if flagSet.err != nil { + t.Fatalf("unexpected error: %v", flagSet.err) + } + if got := v.Map["key"]; got != "val" { + t.Errorf(`map value = %q, want "val"`, got) + } +} diff --git a/flag.go b/flag.go index 55b71cf..b57c0e4 100644 --- a/flag.go +++ b/flag.go @@ -436,7 +436,7 @@ func splitKeyVal(s, sep string) (key, val string, err error) { if index == -1 { return s, "", nil } - return s[:index], s[index+1:], nil + return s[:index], s[index+len(sep):], nil } func minmaxIntCheck(kind reflect.Kind, v int64) bool {