@@ -659,11 +659,14 @@ func Main(sout io.Writer, serr io.Writer, version string, applySleepDurationStri
659659 return err
660660 }
661661
662- parser .Options |= flags .HelpFlag
663-
664- _ , err = parser .ParseArgs (args )
665-
662+ // Strict flag validation block
663+ err = validateCommandFlags (parser , args )
666664 if err != nil {
665+ return err
666+ }
667+
668+ parser .Options |= flags .HelpFlag
669+ if _ , err = parser .ParseArgs (args ); err != nil {
667670 if e , ok := err .(* flags.Error ); ok {
668671 switch e .Type {
669672 case flags .ErrHelp , flags .ErrCommandRequired :
@@ -787,3 +790,121 @@ func checkForVars(opts *options) error {
787790
788791 return nil
789792}
793+
794+ // validateCommandFlags checks if the provided command flags are valid for the given command.
795+ func validateCommandFlags (parser * flags.Parser , args []string ) error {
796+ // If no args, return nil
797+ if len (args ) == 0 {
798+ return nil
799+ }
800+
801+ // Find the command to validate flags for
802+ cmdName := args [0 ]
803+ var selectedCmd * flags.Command
804+ for _ , cmd := range parser .Commands () {
805+ if cmd .Name == cmdName || contains (cmd .Aliases , cmdName ) {
806+ selectedCmd = cmd
807+ break
808+ }
809+ }
810+ // Unknown command, let parser handle it
811+ if selectedCmd == nil {
812+ return nil
813+ }
814+
815+ // Find unknown flags
816+ invalidFlags := findUnknownFlags (selectedCmd , args )
817+
818+ // If there are unknown flags, print an error and return
819+ if len (invalidFlags ) > 0 {
820+ fmt .Fprintf (os .Stderr , "Error: unknown flag(s) %q for command '%s'\n " , invalidFlags , selectedCmd .Name )
821+ fmt .Fprintf (os .Stderr , "See 'om %s --help' for available options.\n " , selectedCmd .Name )
822+ return fmt .Errorf ("unknown flag(s) %q for command '%s'" , invalidFlags , selectedCmd .Name )
823+ }
824+ return nil
825+ }
826+
827+ // findUnknownFlags checks for unknown flags in the provided args for the given command.
828+ func findUnknownFlags (selectedCmd * flags.Command , args []string ) []string {
829+ validFlags := make (map [string ]bool )
830+ addFlag := func (name string , takesValue bool ) {
831+ validFlags [name ] = takesValue
832+ }
833+ for _ , opt := range selectedCmd .Options () {
834+ val := opt .Value ()
835+ _ , isBool := val .(* bool )
836+ _ , isBoolSlice := val .(* []bool )
837+ takesValue := ! (isBool || isBoolSlice )
838+ if ln := opt .LongNameWithNamespace (); ln != "" {
839+ addFlag ("--" + ln , takesValue )
840+ }
841+ if opt .ShortName != 0 {
842+ addFlag ("-" + string (opt .ShortName ), takesValue )
843+ }
844+ }
845+ addFlag ("--help" , false )
846+ addFlag ("-h" , false )
847+
848+ var invalidFlags []string
849+ i := 1
850+ for i < len (args ) {
851+ arg := args [i ]
852+ if ! strings .HasPrefix (arg , "-" ) {
853+ // Not a flag, just a value
854+ // Example: args = ["upload-product", "file.pivotal"]
855+ // "file.pivotal" is a positional argument or a value for a previous flag
856+ i ++
857+ continue
858+ }
859+
860+ // Split flag and value if --flag=value
861+ flagName , hasEquals := arg , false
862+ if eqIdx := strings .Index (arg , "=" ); eqIdx != - 1 {
863+ flagName = arg [:eqIdx ]
864+ hasEquals = true
865+ // Example: arg = "--product=foo.pivotal" -> flagName = "--product", value = "foo.pivotal"
866+ }
867+
868+ takesValue , isValid := validFlags [flagName ]
869+ if ! isValid {
870+ // Unknown flag
871+ // Example: arg = "--notaflag" (not defined in command options)
872+ invalidFlags = append (invalidFlags , flagName )
873+ i ++
874+ continue
875+ }
876+
877+ if takesValue {
878+ if hasEquals {
879+ // --flag=value, value is in this arg
880+ // Example: arg = "--product=foo.pivotal"
881+ i ++
882+ } else if i + 1 < len (args ) {
883+ // --flag value, value is next arg (even if it looks like a flag)
884+ // Example: args = ["--product", "--notaflag"]
885+ // "--notaflag" is treated as the value for --product, not as a flag
886+ i += 2
887+ } else {
888+ // --flag with missing value.
889+ // No need to handle this here as this will handled appropriately by the parser.
890+ // Example: args = ["--product"] (no value provided)
891+ i ++
892+ }
893+ } else {
894+ // Boolean flag, no value expected
895+ // Example: arg = "--help"
896+ i ++
897+ }
898+ }
899+ return invalidFlags
900+ }
901+
902+ // contains checks if a string is present in a list of strings.
903+ func contains (list []string , s string ) bool {
904+ for _ , v := range list {
905+ if v == s {
906+ return true
907+ }
908+ }
909+ return false
910+ }
0 commit comments