Skip to content

Commit ca510b2

Browse files
committed
SECVULN Fix for git checkout argument injection enables arbitrary file read
1 parent 5971b14 commit ca510b2

File tree

5 files changed

+46
-3
lines changed

5 files changed

+46
-3
lines changed

.release/ci.hcl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ project "go-getter" {
44
team = "team-ip-compliance"
55

66
slack {
7-
notification_channel = "C09KTF77K6X" // ensure this is a PUBLIC slack channel. If it's private, the promotion workflows will fail.
7+
notification_channel = "C09KU972BDH" // ensure this is a PUBLIC slack channel. If it's private, the promotion workflows will fail.
88
}
99

1010
github {

get_git.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,10 @@ func (g *GitGetter) GetFile(dst string, u *url.URL) error {
174174
}
175175

176176
func (g *GitGetter) checkout(ctx context.Context, dst string, ref string) error {
177+
if strings.HasPrefix(ref, "-") {
178+
return fmt.Errorf("invalid ref: %q", ref)
179+
}
180+
177181
cmd := exec.CommandContext(ctx, "git", "checkout", ref, "--")
178182
cmd.Dir = dst
179183
return getRunCommand(cmd)

get_git_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1248,6 +1248,45 @@ func (r *gitRepo) latestCommit() (string, error) {
12481248
return string(rawOut), nil
12491249
}
12501250

1251+
// Test checkout ref option injection to verify that if a user tries to inject a git option
1252+
func TestGitGetter_checkoutRefOptionInjectionRejected(t *testing.T) {
1253+
if !testHasGit {
1254+
t.Skip("git not found, skipping")
1255+
}
1256+
1257+
g := new(GitGetter)
1258+
repo := testGitRepo(t, "empty-repo")
1259+
repo.git("config", "commit.gpgsign", "false")
1260+
repo.commitFile("safe.txt", "safe")
1261+
1262+
// Create a fake "secret" file to simulate sensitive data
1263+
secretPath := filepath.Join(t.TempDir(), "secret.txt")
1264+
secretLine := "THIS_IS_A_SECRET"
1265+
if err := os.WriteFile(secretPath, []byte(secretLine+"\n"), 0600); err != nil {
1266+
t.Fatal(err)
1267+
}
1268+
1269+
// Attempt to inject a git option
1270+
ref := "--pathspec-from-file=" + secretPath
1271+
1272+
err := g.checkout(context.Background(), repo.dir, ref)
1273+
1274+
// Expect error due to validation (not git execution)
1275+
if err == nil {
1276+
t.Fatal("expected error for invalid ref, got nil")
1277+
}
1278+
1279+
// Ensure it's our validation error, not git output
1280+
if !strings.Contains(err.Error(), "invalid ref") {
1281+
t.Fatalf("expected validation error, got: %v", err)
1282+
}
1283+
1284+
// Ensure secret is NOT leaked
1285+
if strings.Contains(err.Error(), secretLine) {
1286+
t.Fatalf("secret leaked in error message:\n%s", err.Error())
1287+
}
1288+
}
1289+
12511290
// This is a read-only deploy key for an empty test repository.
12521291
// Note: This is split over multiple lines to avoid being disabled by key
12531292
// scanners automatically.

version/VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.8.5
1+
1.8.6

version/version.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
package version
55

66
var (
7-
Version = "1.8.5"
7+
Version = "1.8.6"
88
VersionPrerelease = ""
99
VersionMetadata = ""
1010
// PluginVersion removed to avoid import cycle

0 commit comments

Comments
 (0)