diff --git a/internal/tool/code_search.go b/internal/tool/code_search.go index 4e8ee794..20029a2d 100644 --- a/internal/tool/code_search.go +++ b/internal/tool/code_search.go @@ -78,7 +78,14 @@ func (p *CodeSearchProvider) buildGrepArgs(searchText string, caseSensitive bool cmdArgs = append(cmdArgs, "-e", searchText) if ref := p.FileReader.Ref; ref != "" { - cmdArgs = append(cmdArgs, "--end-of-options") + if strings.HasPrefix(ref, "-") { + // Defense-in-depth: reject option-like refs here even though + // validateReviewRefs already verifies the ref upstream. + // NOTE: git grep < 2.45 does not support --end-of-options before + // the revision, so this is the one git invocation where we can't + // rely on that separator. + return nil + } cmdArgs = append(cmdArgs, ref) } @@ -125,6 +132,9 @@ func (p *CodeSearchProvider) runGitGrep(parentCtx context.Context, cmdArgs []str func (p *CodeSearchProvider) gitGrep(ctx context.Context, searchText string, caseSensitive bool, usePerlRegexp bool, pathspec []string) (string, error) { cmdArgs := p.buildGrepArgs(searchText, caseSensitive, usePerlRegexp, false, pathspec) + if cmdArgs == nil { + return "Error: ref must not start with '-'", nil + } outStr, errStr, err := p.runGitGrep(ctx, cmdArgs) diff --git a/internal/tool/code_search_test.go b/internal/tool/code_search_test.go index 7e2e2cf1..6d65db05 100644 --- a/internal/tool/code_search_test.go +++ b/internal/tool/code_search_test.go @@ -33,15 +33,28 @@ func TestBuildGrepArgs_CommitMode(t *testing.T) { p := NewCodeSearch(&FileReader{RepoDir: "/tmp", Ref: "abc1234"}) args := p.buildGrepArgs("myFunc", false, false, false, []string{"pkg/"}) - assertContainsInOrder(t, args, "-e", "myFunc", "--end-of-options", "abc1234", "--", "pkg/") + assertContainsInOrder(t, args, "-e", "myFunc", "abc1234", "--", "pkg/") assertNotContains(t, args, "--untracked") + assertNotContains(t, args, "--end-of-options") } -func TestBuildGrepArgs_RefUsesEndOfOptions(t *testing.T) { +func TestBuildGrepArgs_RejectsOptionLikeRef(t *testing.T) { p := NewCodeSearch(&FileReader{RepoDir: "/tmp", Ref: "-O./pwn.sh"}) args := p.buildGrepArgs("myFunc", false, false, false, nil) + if args != nil { + t.Fatalf("expected buildGrepArgs to return nil for option-like ref, got %v", args) + } +} - assertContainsInOrder(t, args, "-e", "myFunc", "--end-of-options", "-O./pwn.sh", "--") +func TestGitGrep_RejectsOptionLikeRef(t *testing.T) { + p := NewCodeSearch(&FileReader{RepoDir: "/tmp", Ref: "-O./pwn.sh"}) + result, err := p.gitGrep(context.Background(), "myFunc", false, false, nil) + if err != nil { + t.Fatal(err) + } + if result != "Error: ref must not start with '-'" { + t.Fatalf("unexpected result: %s", result) + } } func TestBuildGrepArgs_PatternStartingWithDash(t *testing.T) {