From 677af6ac57295a9e8795a8ab3f29e284f2247fb6 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 15 Oct 2022 20:18:31 +0800 Subject: [PATCH] Follow improve code quality (#21465) After some discussion, introduce a new slice `brokenArgs` to make `gitCmd.Run()` return errors if any dynamic argument is invalid. Co-authored-by: delvh --- modules/git/command.go | 20 +++++++++++++++++--- modules/git/command_test.go | 14 ++++++-------- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/modules/git/command.go b/modules/git/command.go index 8ebd8898fb..ed06dd9b08 100644 --- a/modules/git/command.go +++ b/modules/git/command.go @@ -40,6 +40,7 @@ type Command struct { parentContext context.Context desc string globalArgsLength int + brokenArgs []string } func (c *Command) String() string { @@ -50,6 +51,7 @@ func (c *Command) String() string { } // NewCommand creates and returns a new Git Command based on given command and arguments. +// Each argument should be safe to be trusted. User-provided arguments should be passed to AddDynamicArguments instead. func NewCommand(ctx context.Context, args ...string) *Command { // Make an explicit copy of globalCommandArgs, otherwise append might overwrite it cargs := make([]string, len(globalCommandArgs)) @@ -63,11 +65,13 @@ func NewCommand(ctx context.Context, args ...string) *Command { } // NewCommandNoGlobals creates and returns a new Git Command based on given command and arguments only with the specify args and don't care global command args +// Each argument should be safe to be trusted. User-provided arguments should be passed to AddDynamicArguments instead. func NewCommandNoGlobals(args ...string) *Command { return NewCommandContextNoGlobals(DefaultContext, args...) } // NewCommandContextNoGlobals creates and returns a new Git Command based on given command and arguments only with the specify args and don't care global command args +// Each argument should be safe to be trusted. User-provided arguments should be passed to AddDynamicArguments instead. func NewCommandContextNoGlobals(ctx context.Context, args ...string) *Command { return &Command{ name: GitExecutable, @@ -89,20 +93,24 @@ func (c *Command) SetDescription(desc string) *Command { return c } -// AddArguments adds new argument(s) to the command. +// AddArguments adds new argument(s) to the command. Each argument must be safe to be trusted. +// User-provided arguments should be passed to AddDynamicArguments instead. func (c *Command) AddArguments(args ...string) *Command { c.args = append(c.args, args...) return c } // AddDynamicArguments adds new dynamic argument(s) to the command. -// If the argument is invalid (it shouldn't happen in real life), it panics to caller +// The arguments may come from user input and can not be trusted, so no leading '-' is allowed to avoid passing options func (c *Command) AddDynamicArguments(args ...string) *Command { for _, arg := range args { if arg != "" && arg[0] == '-' { - panic("invalid argument: " + arg) + c.brokenArgs = append(c.brokenArgs, arg) } } + if len(c.brokenArgs) != 0 { + return c + } c.args = append(c.args, args...) return c } @@ -150,8 +158,14 @@ func CommonCmdServEnvs() []string { return commonBaseEnvs() } +var ErrBrokenCommand = errors.New("git command is broken") + // Run runs the command with the RunOpts func (c *Command) Run(opts *RunOpts) error { + if len(c.brokenArgs) != 0 { + log.Error("git command is broken: %s, broken args: %s", c.String(), strings.Join(c.brokenArgs, " ")) + return ErrBrokenCommand + } if opts == nil { opts = &RunOpts{} } diff --git a/modules/git/command_test.go b/modules/git/command_test.go index c965ea230d..52d25c9c74 100644 --- a/modules/git/command_test.go +++ b/modules/git/command_test.go @@ -27,15 +27,13 @@ func TestRunWithContextStd(t *testing.T) { assert.Empty(t, stdout) } - assert.Panics(t, func() { - cmd = NewCommand(context.Background()) - cmd.AddDynamicArguments("-test") - }) + cmd = NewCommand(context.Background()) + cmd.AddDynamicArguments("-test") + assert.ErrorIs(t, cmd.Run(&RunOpts{}), ErrBrokenCommand) - assert.Panics(t, func() { - cmd = NewCommand(context.Background()) - cmd.AddDynamicArguments("--test") - }) + cmd = NewCommand(context.Background()) + cmd.AddDynamicArguments("--test") + assert.ErrorIs(t, cmd.Run(&RunOpts{}), ErrBrokenCommand) subCmd := "version" cmd = NewCommand(context.Background()).AddDynamicArguments(subCmd) // for test purpose only, the sub-command should never be dynamic for production