Skip to content

Commit cd48a00

Browse files
6543wxiaoguang
andauthored
improve code quality (#21464) (#21463)
Backport #21464 and #21465 Co-authored-by: wxiaoguang <[email protected]>
1 parent 6afbef5 commit cd48a00

File tree

6 files changed

+64
-27
lines changed

6 files changed

+64
-27
lines changed

modules/git/command.go

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ type Command struct {
4040
parentContext context.Context
4141
desc string
4242
globalArgsLength int
43+
brokenArgs []string
4344
}
4445

4546
func (c *Command) String() string {
@@ -50,6 +51,7 @@ func (c *Command) String() string {
5051
}
5152

5253
// NewCommand creates and returns a new Git Command based on given command and arguments.
54+
// Each argument should be safe to be trusted. User-provided arguments should be passed to AddDynamicArguments instead.
5355
func NewCommand(ctx context.Context, args ...string) *Command {
5456
// Make an explicit copy of globalCommandArgs, otherwise append might overwrite it
5557
cargs := make([]string, len(globalCommandArgs))
@@ -63,11 +65,13 @@ func NewCommand(ctx context.Context, args ...string) *Command {
6365
}
6466

6567
// 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
68+
// Each argument should be safe to be trusted. User-provided arguments should be passed to AddDynamicArguments instead.
6669
func NewCommandNoGlobals(args ...string) *Command {
6770
return NewCommandContextNoGlobals(DefaultContext, args...)
6871
}
6972

7073
// 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
74+
// Each argument should be safe to be trusted. User-provided arguments should be passed to AddDynamicArguments instead.
7175
func NewCommandContextNoGlobals(ctx context.Context, args ...string) *Command {
7276
return &Command{
7377
name: GitExecutable,
@@ -89,12 +93,28 @@ func (c *Command) SetDescription(desc string) *Command {
8993
return c
9094
}
9195

92-
// AddArguments adds new argument(s) to the command.
96+
// AddArguments adds new argument(s) to the command. Each argument must be safe to be trusted.
97+
// User-provided arguments should be passed to AddDynamicArguments instead.
9398
func (c *Command) AddArguments(args ...string) *Command {
9499
c.args = append(c.args, args...)
95100
return c
96101
}
97102

103+
// AddDynamicArguments adds new dynamic argument(s) to the command.
104+
// The arguments may come from user input and can not be trusted, so no leading '-' is allowed to avoid passing options
105+
func (c *Command) AddDynamicArguments(args ...string) *Command {
106+
for _, arg := range args {
107+
if arg != "" && arg[0] == '-' {
108+
c.brokenArgs = append(c.brokenArgs, arg)
109+
}
110+
}
111+
if len(c.brokenArgs) != 0 {
112+
return c
113+
}
114+
c.args = append(c.args, args...)
115+
return c
116+
}
117+
98118
// RunOpts represents parameters to run the command. If UseContextTimeout is specified, then Timeout is ignored.
99119
type RunOpts struct {
100120
Env []string
@@ -138,8 +158,14 @@ func CommonCmdServEnvs() []string {
138158
return commonBaseEnvs()
139159
}
140160

161+
var ErrBrokenCommand = errors.New("git command is broken")
162+
141163
// Run runs the command with the RunOpts
142164
func (c *Command) Run(opts *RunOpts) error {
165+
if len(c.brokenArgs) != 0 {
166+
log.Error("git command is broken: %s, broken args: %s", c.String(), strings.Join(c.brokenArgs, " "))
167+
return ErrBrokenCommand
168+
}
143169
if opts == nil {
144170
opts = &RunOpts{}
145171
}

modules/git/command_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,19 @@ func TestRunWithContextStd(t *testing.T) {
2626
assert.Contains(t, err.Error(), "exit status 129 - unknown option:")
2727
assert.Empty(t, stdout)
2828
}
29+
30+
cmd = NewCommand(context.Background())
31+
cmd.AddDynamicArguments("-test")
32+
assert.ErrorIs(t, cmd.Run(&RunOpts{}), ErrBrokenCommand)
33+
34+
cmd = NewCommand(context.Background())
35+
cmd.AddDynamicArguments("--test")
36+
assert.ErrorIs(t, cmd.Run(&RunOpts{}), ErrBrokenCommand)
37+
38+
subCmd := "version"
39+
cmd = NewCommand(context.Background()).AddDynamicArguments(subCmd) // for test purpose only, the sub-command should never be dynamic for production
40+
stdout, stderr, err = cmd.RunStdString(&RunOpts{})
41+
assert.NoError(t, err)
42+
assert.Empty(t, stderr)
43+
assert.Contains(t, stdout, "git version")
2944
}

modules/git/commit.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ func AllCommitsCount(ctx context.Context, repoPath string, hidePRRefs bool, file
163163
// CommitsCountFiles returns number of total commits of until given revision.
164164
func CommitsCountFiles(ctx context.Context, repoPath string, revision, relpath []string) (int64, error) {
165165
cmd := NewCommand(ctx, "rev-list", "--count")
166-
cmd.AddArguments(revision...)
166+
cmd.AddDynamicArguments(revision...)
167167
if len(relpath) > 0 {
168168
cmd.AddArguments("--")
169169
cmd.AddArguments(relpath...)

modules/git/repo_commit.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ func (repo *Repository) searchCommits(id SHA1, opts SearchCommitsOptions) ([]*Co
158158
// add previous arguments except for --grep and --all
159159
hashCmd.AddArguments(args...)
160160
// add keyword as <commit>
161-
hashCmd.AddArguments(v)
161+
hashCmd.AddDynamicArguments(v)
162162

163163
// search with given constraints for commit matching sha hash of v
164164
hashMatching, _, err := hashCmd.RunStdBytes(&RunOpts{Dir: repo.Path})
@@ -208,14 +208,15 @@ func (repo *Repository) CommitsByFileAndRange(revision, file string, page int) (
208208
}()
209209
go func() {
210210
stderr := strings.Builder{}
211-
err := NewCommand(repo.Ctx, "log", revision, "--follow",
212-
"--max-count="+strconv.Itoa(setting.Git.CommitsRangeSize*page),
213-
prettyLogFormat, "--", file).
214-
Run(&RunOpts{
215-
Dir: repo.Path,
216-
Stdout: stdoutWriter,
217-
Stderr: &stderr,
218-
})
211+
gitCmd := NewCommand(repo.Ctx, "log", prettyLogFormat, "--follow",
212+
"--max-count="+strconv.Itoa(setting.Git.CommitsRangeSize*page))
213+
gitCmd.AddDynamicArguments(revision)
214+
gitCmd.AddArguments("--", file)
215+
err := gitCmd.Run(&RunOpts{
216+
Dir: repo.Path,
217+
Stdout: stdoutWriter,
218+
Stderr: &stderr,
219+
})
219220
if err != nil {
220221
_ = stdoutWriter.CloseWithError(ConcatenateError(err, (&stderr).String()))
221222
} else {

modules/git/repo_stats.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,15 +59,15 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string)
5959
_ = stdoutWriter.Close()
6060
}()
6161

62-
args := []string{"log", "--numstat", "--no-merges", "--pretty=format:---%n%h%n%aN%n%aE%n", "--date=iso", fmt.Sprintf("--since='%s'", since)}
62+
gitCmd := NewCommand(repo.Ctx, "log", "--numstat", "--no-merges", "--pretty=format:---%n%h%n%aN%n%aE%n", "--date=iso", fmt.Sprintf("--since='%s'", since))
6363
if len(branch) == 0 {
64-
args = append(args, "--branches=*")
64+
gitCmd.AddArguments("--branches=*")
6565
} else {
66-
args = append(args, "--first-parent", branch)
66+
gitCmd.AddArguments("--first-parent").AddDynamicArguments(branch)
6767
}
6868

6969
stderr := new(strings.Builder)
70-
err = NewCommand(repo.Ctx, args...).Run(&RunOpts{
70+
err = gitCmd.Run(&RunOpts{
7171
Env: []string{},
7272
Dir: repo.Path,
7373
Stdout: stdoutWriter,

modules/gitgraph/graph.go

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,35 +24,30 @@ func GetCommitGraph(r *git.Repository, page, maxAllowedColors int, hidePRRefs bo
2424
page = 1
2525
}
2626

27-
args := make([]string, 0, 12+len(branches)+len(files))
28-
29-
args = append(args, "--graph", "--date-order", "--decorate=full")
27+
graphCmd := git.NewCommand(r.Ctx, "log", "--graph", "--date-order", "--decorate=full")
3028

3129
if hidePRRefs {
32-
args = append(args, "--exclude="+git.PullPrefix+"*")
30+
graphCmd.AddArguments("--exclude=" + git.PullPrefix + "*")
3331
}
3432

3533
if len(branches) == 0 {
36-
args = append(args, "--all")
34+
graphCmd.AddArguments("--all")
3735
}
3836

39-
args = append(args,
37+
graphCmd.AddArguments(
4038
"-C",
4139
"-M",
4240
fmt.Sprintf("-n %d", setting.UI.GraphMaxCommitNum*page),
4341
"--date=iso",
4442
fmt.Sprintf("--pretty=format:%s", format))
4543

4644
if len(branches) > 0 {
47-
args = append(args, branches...)
45+
graphCmd.AddDynamicArguments(branches...)
4846
}
49-
args = append(args, "--")
5047
if len(files) > 0 {
51-
args = append(args, files...)
48+
graphCmd.AddArguments("--")
49+
graphCmd.AddArguments(files...)
5250
}
53-
54-
graphCmd := git.NewCommand(r.Ctx, "log")
55-
graphCmd.AddArguments(args...)
5651
graph := NewGraph()
5752

5853
stderr := new(strings.Builder)

0 commit comments

Comments
 (0)