internal/git/gitcmd/command.go (41 lines of code) (raw):

package gitcmd import ( "errors" "fmt" "regexp" ) var ( // ErrInvalidArg represent family of errors to report about bad argument used to make a call. ErrInvalidArg = errors.New("invalid argument") // ErrHookPayloadRequired indicates a HookPayload is needed but // absent from the command. ErrHookPayloadRequired = errors.New("hook payload is required but not configured") actionRegex = regexp.MustCompile(`^[[:alnum:]]+[-[:alnum:]]*$`) ) // Command represent a Git command. type Command struct { // Name is the name of the Git command to run, e.g. "log", "cat-file" or "worktree". Name string // Action is the action of the Git command, e.g. "set-url" in `git remote set-url` Action string // Flags is the number of optional flags to pass before positional arguments, e.g. // `--oneline` or `--format=fuller`. Flags []Option // Args is the arguments that shall be passed after all flags. These arguments must not be // flags and thus cannot start with `-`. Note that it may be unsafe to use this field in the // case where arguments are directly user-controlled. In that case it is advisable to use // `PostSepArgs` instead. Args []string // PostSepArgs is the arguments that shall be passed as positional arguments after the `--` // separator. Git recognizes that separator as the point where it should stop expecting any // options and treat the remaining arguments as positionals. This should be used when // passing user-controlled input of arbitrary form like for example paths, which may start // with a `-`. PostSepArgs []string } // CommandArgs checks all arguments in the sub command and validates them func (c Command) CommandArgs() ([]string, error) { var safeArgs []string commandDescription, ok := commandDescriptions[c.Name] if !ok { return nil, fmt.Errorf("invalid sub command name %q: %w", c.Name, ErrInvalidArg) } safeArgs = append(safeArgs, c.Name) if c.Action != "" { if !actionRegex.MatchString(c.Action) { return nil, fmt.Errorf("invalid action %q: %w", c.Action, ErrInvalidArg) } safeArgs = append(safeArgs, c.Action) } commandArgs, err := commandDescription.args(c.Flags, c.Args, c.PostSepArgs) if err != nil { return nil, err } safeArgs = append(safeArgs, commandArgs...) return safeArgs, nil } // IsInvalidArgErr relays if the error is due to an argument validation failure func IsInvalidArgErr(err error) bool { return errors.Is(err, ErrInvalidArg) }