diff --git a/modules/git/command.go b/modules/git/command.go index 22cb275ab..a3d43aaec 100644 --- a/modules/git/command.go +++ b/modules/git/command.go @@ -153,6 +153,18 @@ func (c *Command) AddOptionValues(opt internal.CmdArg, args ...string) *Command return c } +// AddGitGrepExpression adds an expression option (-e) to git-grep command +// It is different from AddOptionValues in that it allows the actual expression +// to not be filtered out for leading dashes (which is otherwise a security feature +// of AddOptionValues). +func (c *Command) AddGitGrepExpression(exp string) *Command { + if c.args[len(globalCommandArgs)] != "grep" { + panic("function called on a non-grep git program: " + c.args[0]) + } + c.args = append(c.args, "-e", exp) + return c +} + // AddOptionFormat adds a new option with a format string and arguments // For example: AddOptionFormat("--opt=%s %s", val1, val2) means 1 argument: {"--opt=val1 val2"}. func (c *Command) AddOptionFormat(opt string, args ...any) *Command { diff --git a/modules/git/command_test.go b/modules/git/command_test.go index 1d8d3bc12..d3b8338d0 100644 --- a/modules/git/command_test.go +++ b/modules/git/command_test.go @@ -61,3 +61,10 @@ func TestCommandString(t *testing.T) { cmd = NewCommandContextNoGlobals(context.Background(), "url: https://a:b@c/") assert.EqualValues(t, cmd.prog+` "url: https://sanitized-credential@c/"`, cmd.toString(true)) } + +func TestGrepOnlyFunction(t *testing.T) { + cmd := NewCommand(context.Background(), "anything-but-grep") + assert.Panics(t, func() { + cmd.AddGitGrepExpression("whatever") + }) +} diff --git a/modules/git/grep.go b/modules/git/grep.go index 8c2cfd54b..93423a85c 100644 --- a/modules/git/grep.go +++ b/modules/git/grep.go @@ -76,7 +76,7 @@ func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepO words = strings.Fields(search) } for _, word := range words { - cmd.AddOptionValues("-e", strings.TrimLeft(word, "-")) + cmd.AddGitGrepExpression(word) } // pathspec diff --git a/modules/git/grep_test.go b/modules/git/grep_test.go index 5d0d343ac..3ba7a6efc 100644 --- a/modules/git/grep_test.go +++ b/modules/git/grep_test.go @@ -98,6 +98,31 @@ func TestGrepSearch(t *testing.T) { assert.Empty(t, res) } +func TestGrepDashesAreFine(t *testing.T) { + tmpDir := t.TempDir() + + err := InitRepository(DefaultContext, tmpDir, false, Sha1ObjectFormat.Name()) + require.NoError(t, err) + + gitRepo, err := openRepositoryWithDefaultContext(tmpDir) + require.NoError(t, err) + defer gitRepo.Close() + + require.NoError(t, os.WriteFile(path.Join(tmpDir, "with-dashes"), []byte("--"), 0o666)) + require.NoError(t, os.WriteFile(path.Join(tmpDir, "without-dashes"), []byte(".."), 0o666)) + + err = AddChanges(tmpDir, true) + require.NoError(t, err) + + err = CommitChanges(tmpDir, CommitChangesOptions{Message: "Dashes are cool sometimes"}) + require.NoError(t, err) + + res, err := GrepSearch(context.Background(), gitRepo, "--", GrepOptions{}) + require.NoError(t, err) + assert.Len(t, res, 1) + assert.Equal(t, "with-dashes", res[0].Filename) +} + func TestGrepNoBinary(t *testing.T) { tmpDir := t.TempDir()