Revert "[INS-397] Fix git version parser panic on non-numeric patch versions …" (#4903)
Lint / golangci-lint (push) Waiting to run
Lint / man-page-staleness (push) Waiting to run
Lint / semgrep (push) Waiting to run
Scan for secrets / test (push) Waiting to run
Test / test (push) Waiting to run
Test / test-community (push) Waiting to run
Release / mark-latest (push) Canceled after 0s
Release / Release (push) Canceled after 0s

This reverts commit c120e8ccba.
This commit is contained in:
trufflesteeeve
2026-04-21 16:27:43 -04:00
committed by GitHub
parent 2b8081980d
commit 17456f8c7d
9 changed files with 77 additions and 137 deletions
@@ -3,8 +3,10 @@ package repositorykey
import (
"context"
"errors"
"fmt"
"net/url"
"os/exec"
"strconv"
"strings"
regexp "github.com/wasilibs/go-re2"
@@ -12,7 +14,6 @@ import (
"github.com/trufflesecurity/trufflehog/v3/pkg/cache/simple"
logContext "github.com/trufflesecurity/trufflehog/v3/pkg/context"
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
"github.com/trufflesecurity/trufflehog/v3/pkg/gitcmd"
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detector_typepb"
)
@@ -99,8 +100,36 @@ func (s Scanner) Description() string {
return "Azure API Management Repository Keys provide access to the API Management (APIM) configuration repository, allowing users to directly interact with and modify API definitions, policies, and settings. These keys enable programmatic access to APIM's Git-based repository, where configurations can be cloned, edited, and pushed back to apply changes. They are primarily used for managing API configurations as code, automating deployments, and synchronizing APIM settings across environments."
}
func gitCmdCheck() error {
if errors.Is(exec.Command("git").Run(), exec.ErrNotFound) {
return fmt.Errorf("'git' command not found in $PATH. Make sure git is installed and included in $PATH")
}
// Check the version is greater than or equal to 2.20.0
out, err := exec.Command("git", "--version").Output()
if err != nil {
return fmt.Errorf("failed to check git version: %w", err)
}
// Extract the version string using a regex to find the version numbers
var regex = regexp.MustCompile(`\d+\.\d+\.\d+`)
versionStr := regex.FindString(string(out))
versionParts := strings.Split(versionStr, ".")
// Parse version numbers
major, _ := strconv.Atoi(versionParts[0])
minor, _ := strconv.Atoi(versionParts[1])
// Compare with version 2.20.0<=x<3.0.0
if major == 2 && minor >= 20 {
return nil
}
return fmt.Errorf("git version is %s, but must be greater than or equal to 2.20.0, and less than 3.0.0", versionStr)
}
func verifyUrlPassword(_ context.Context, repoUrl, user, password string) (bool, error) {
if err := gitcmd.CheckVersion(); err != nil {
if err := gitCmdCheck(); err != nil {
return false, err
}
-53
View File
@@ -1,53 +0,0 @@
// Package gitcmd provides helpers for interacting with the local git binary.
package gitcmd
import (
"fmt"
"os/exec"
"regexp"
"strconv"
"strings"
"github.com/go-errors/errors"
)
// gitVersionRegex captures the major and minor numbers from `git --version` output.
// We intentionally do not capture the patch version: git built from source can report
// a non-numeric patch like "git version 2.52.gaea8cc3", and the patch is unused here.
var gitVersionRegex = regexp.MustCompile(`(\d+)\.(\d+)`)
// CheckVersion checks if git is installed and meets 2.20.0<=x<3.0.0 version requirements.
func CheckVersion() error {
if errors.Is(exec.Command("git").Run(), exec.ErrNotFound) {
return fmt.Errorf("'git' command not found in $PATH. Make sure git is installed and included in $PATH")
}
// Check the version is greater than or equal to 2.20.0
out, err := exec.Command("git", "--version").Output()
if err != nil {
return fmt.Errorf("failed to check git version: %w", err)
}
major, minor, err := parseGitVersion(string(out))
if err != nil {
return err
}
// Compare with version 2.20.0<=x<3.0.0
if major == 2 && minor >= 20 {
return nil
}
return fmt.Errorf("git version is %d.%d, but must be greater than or equal to 2.20.0, and less than 3.0.0", major, minor)
}
// parseGitVersion extracts the major and minor numbers from `git --version` output.
func parseGitVersion(out string) (major, minor int, err error) {
matches := gitVersionRegex.FindStringSubmatch(out)
if len(matches) < 3 {
return 0, 0, fmt.Errorf("failed to parse git version from %q", strings.TrimSpace(out))
}
// Errors are impossible here since the regex only matches digits.
major, _ = strconv.Atoi(matches[1])
minor, _ = strconv.Atoi(matches[2])
return major, minor, nil
}
-71
View File
@@ -1,71 +0,0 @@
package gitcmd
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestParseGitVersion(t *testing.T) {
tests := []struct {
name string
out string
wantMajor int
wantMinor int
wantErr bool
}{
{
name: "standard semver",
out: "git version 2.34.1\n",
wantMajor: 2,
wantMinor: 34,
},
{
name: "non-numeric patch (built from source)",
out: "git version 2.52.gaea8cc3\n",
wantMajor: 2,
wantMinor: 52,
},
{
name: "apple git suffix",
out: "git version 2.39.2 (Apple Git-143)\n",
wantMajor: 2,
wantMinor: 39,
},
{
name: "windows git suffix",
out: "git version 2.39.2.windows.1\n",
wantMajor: 2,
wantMinor: 39,
},
{
name: "no patch component",
out: "git version 2.20\n",
wantMajor: 2,
wantMinor: 20,
},
{
name: "no version present",
out: "git is not a version\n",
wantErr: true,
},
{
name: "empty output",
out: "",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
major, minor, err := parseGitVersion(tt.out)
if tt.wantErr {
assert.Error(t, err)
return
}
assert.NoError(t, err)
assert.Equal(t, tt.wantMajor, major)
assert.Equal(t, tt.wantMinor, minor)
})
}
}
+40
View File
@@ -0,0 +1,40 @@
package git
import (
"fmt"
"os/exec"
"regexp"
"strconv"
"strings"
"github.com/go-errors/errors"
)
// Extract the version string using a regex to find the version numbers
var regex = regexp.MustCompile(`\d+\.\d+\.\d+`)
// CmdCheck checks if git is installed and meets 2.20.0<=x<3.0.0 version requirements.
func CmdCheck() error {
if errors.Is(exec.Command("git").Run(), exec.ErrNotFound) {
return fmt.Errorf("'git' command not found in $PATH. Make sure git is installed and included in $PATH")
}
// Check the version is greater than or equal to 2.20.0
out, err := exec.Command("git", "--version").Output()
if err != nil {
return fmt.Errorf("failed to check git version: %w", err)
}
versionStr := regex.FindString(string(out))
versionParts := strings.Split(versionStr, ".")
// Parse version numbers
major, _ := strconv.Atoi(versionParts[0])
minor, _ := strconv.Atoi(versionParts[1])
// Compare with version 2.20.0<=x<3.0.0
if major == 2 && minor >= 20 {
return nil
}
return fmt.Errorf("git version is %s, but must be greater than or equal to 2.20.0, and less than 3.0.0", versionStr)
}
+2 -3
View File
@@ -30,7 +30,6 @@ import (
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
"github.com/trufflesecurity/trufflehog/v3/pkg/feature"
"github.com/trufflesecurity/trufflehog/v3/pkg/gitcmd"
"github.com/trufflesecurity/trufflehog/v3/pkg/gitparse"
"github.com/trufflesecurity/trufflehog/v3/pkg/handlers"
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/source_metadatapb"
@@ -215,7 +214,7 @@ func (s *Source) Init(aCtx context.Context, name string, jobId sources.JobID, so
concurrency = runtime.NumCPU()
}
if err = gitcmd.CheckVersion(); err != nil {
if err = CmdCheck(); err != nil {
return err
}
@@ -615,7 +614,7 @@ func executeClone(ctx context.Context, params cloneParams) (*git.Repository, err
//
// Pinging using other authentication methods is only unimplemented because there's been no pressing need for it yet.
func PingRepoUsingToken(ctx context.Context, token, gitUrl, user string) error {
if err := gitcmd.CheckVersion(); err != nil {
if err := CmdCheck(); err != nil {
return err
}
lsUrl, err := GitURLParse(gitUrl)
+1 -2
View File
@@ -28,7 +28,6 @@ import (
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
"github.com/trufflesecurity/trufflehog/v3/pkg/feature"
"github.com/trufflesecurity/trufflehog/v3/pkg/gitcmd"
"github.com/trufflesecurity/trufflehog/v3/pkg/giturl"
"github.com/trufflesecurity/trufflehog/v3/pkg/handlers"
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/source_metadatapb"
@@ -214,7 +213,7 @@ func (c *filteredRepoCache) wantRepo(s string) bool {
// Init returns an initialized GitHub source.
func (s *Source) Init(aCtx context.Context, name string, jobID sources.JobID, sourceID sources.SourceID, verify bool, connection *anypb.Any, concurrency int) error {
err := gitcmd.CheckVersion()
err := git.CmdCheck()
if err != nil {
return err
}
@@ -9,7 +9,6 @@ import (
"google.golang.org/protobuf/types/known/anypb"
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
"github.com/trufflesecurity/trufflehog/v3/pkg/gitcmd"
"github.com/trufflesecurity/trufflehog/v3/pkg/giturl"
"github.com/trufflesecurity/trufflehog/v3/pkg/log"
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/source_metadatapb"
@@ -69,7 +68,7 @@ func (s *Source) JobID() sources.JobID {
// Init returns an initialized GitHubExperimental source.
func (s *Source) Init(aCtx context.Context, name string, jobID sources.JobID, sourceID sources.SourceID, verify bool, connection *anypb.Any, concurrency int) error {
err := gitcmd.CheckVersion()
err := git.CmdCheck()
if err != nil {
return err
}
+1 -2
View File
@@ -13,7 +13,6 @@ import (
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
"github.com/trufflesecurity/trufflehog/v3/pkg/feature"
"github.com/trufflesecurity/trufflehog/v3/pkg/gitcmd"
"github.com/trufflesecurity/trufflehog/v3/pkg/giturl"
"github.com/trufflesecurity/trufflehog/v3/pkg/log"
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/source_metadatapb"
@@ -167,7 +166,7 @@ func (s *Source) Init(ctx context.Context, name string, jobId sources.JobID, sou
s.jobPool = &errgroup.Group{}
s.jobPool.SetLimit(concurrency)
if err := gitcmd.CheckVersion(); err != nil {
if err := git.CmdCheck(); err != nil {
return err
}
+1 -2
View File
@@ -19,7 +19,6 @@ import (
"github.com/trufflesecurity/trufflehog/v3/pkg/cache/simple"
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
"github.com/trufflesecurity/trufflehog/v3/pkg/gitcmd"
"github.com/trufflesecurity/trufflehog/v3/pkg/giturl"
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/source_metadatapb"
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/sourcespb"
@@ -170,7 +169,7 @@ func (c *filteredRepoCache) includeRepo(s string) bool {
// Init returns an initialized HuggingFace source.
func (s *Source) Init(ctx context.Context, name string, jobID sources.JobID, sourceID sources.SourceID, verify bool, connection *anypb.Any, concurrency int) error {
err := gitcmd.CheckVersion()
err := git.CmdCheck()
if err != nil {
return err
}