From 67350f5fc1aa34313dcdf726761bd7506d50d139 Mon Sep 17 00:00:00 2001 From: wuyangfan <1102042793@qq.com> Date: Sun, 17 May 2026 20:10:50 +0800 Subject: [PATCH] fix: reject single-dash long flag names When an argument matches a registered long flag name but uses one leading dash (e.g. -name), return a clear error instead of parsing it as shorthand plus a value suffix. Go test flags (-test.*) are exempt. Fixes spf13/cobra#2358 --- errors.go | 16 ++++++++++++++++ flag.go | 12 ++++++++++++ flag_test.go | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+) diff --git a/errors.go b/errors.go index 63765ddc..94658b77 100644 --- a/errors.go +++ b/errors.go @@ -127,6 +127,22 @@ func (e *InvalidValueError) GetValue() string { return e.value } +// LongFlagSingleDashError is returned when a registered long flag name is +// passed with a single leading dash (e.g. -name instead of --name). +type LongFlagSingleDashError struct { + name string +} + +// Error implements error. +func (e *LongFlagSingleDashError) Error() string { + return fmt.Sprintf("flag %q must be specified as --%s (not -%s)", e.name, e.name, e.name) +} + +// GetName returns the long flag name that was mistyped. +func (e *LongFlagSingleDashError) GetName() string { + return e.name +} + // InvalidSyntaxError is the error returned when a bad flag name is passed on // the command line. type InvalidSyntaxError struct { diff --git a/flag.go b/flag.go index dc47fff2..6b927123 100644 --- a/flag.go +++ b/flag.go @@ -1128,6 +1128,18 @@ func (f *FlagSet) parseShortArg(s string, args []string, fn parseFunc) (a []stri a = args shorthands := s[1:] + if len(shorthands) > 1 { + flagName := shorthands + if eq := strings.IndexByte(flagName, '='); eq >= 0 { + flagName = flagName[:eq] + } + if !isGotestShorthandFlag(flagName) { + if _, exists := f.formal[f.normalizeFlagName(flagName)]; exists { + return a, f.fail(&LongFlagSingleDashError{name: flagName}) + } + } + } + // "shorthands" can be a series of shorthand letters of flags (e.g. "-vvv"). for len(shorthands) > 0 { shorthands, a, err = f.parseSingleShortArg(shorthands, args, fn) diff --git a/flag_test.go b/flag_test.go index 282522a3..e9d81370 100644 --- a/flag_test.go +++ b/flag_test.go @@ -6,6 +6,7 @@ package pflag import ( "bytes" + "errors" "fmt" "io" "io/ioutil" @@ -1461,3 +1462,38 @@ func TestVisitFlagOrder(t *testing.T) { i++ }) } + +func TestParseRejectsSingleDashLongFlag(t *testing.T) { + f := NewFlagSet("test", ContinueOnError) + var name string + f.StringVarP(&name, "name", "n", "", "name of configuration") + + err := f.Parse([]string{"-name", "wrong"}) + if err == nil { + t.Fatal("expected error for -name mistyped long flag") + } + var longDash *LongFlagSingleDashError + if !errors.As(err, &longDash) { + t.Fatalf("expected LongFlagSingleDashError, got %T: %v", err, err) + } + if name != "" { + t.Fatalf("expected name to stay empty, got %q", name) + } + + err = f.Parse([]string{"-name=wrong"}) + if err == nil { + t.Fatal("expected error for -name= mistyped long flag") + } + if !errors.As(err, &longDash) { + t.Fatalf("expected LongFlagSingleDashError, got %T: %v", err, err) + } + + f = NewFlagSet("test", ContinueOnError) + f.StringVarP(&name, "name", "n", "", "name of configuration") + if err := f.Parse([]string{"-n", "value"}); err != nil { + t.Fatalf("expected -n value to parse: %v", err) + } + if name != "value" { + t.Fatalf("expected name=value, got %q", name) + } +}