Skip to content

Commit ea2873b

Browse files
committed
hardening
1 parent 56b05a9 commit ea2873b

File tree

2 files changed

+56
-8
lines changed

2 files changed

+56
-8
lines changed

main.go

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111

1212
func main() {
1313
// find the next git after the first one found
14-
os.Args[0] = findGit(os.Getenv("PATH"))
14+
os.Args[0] = FindGit(os.Getenv("PATH"))
1515
if os.Args[0] == "" {
1616
fmt.Fprintln(os.Stderr, "Failed to find git in the path")
1717
os.Exit(1)
@@ -39,7 +39,7 @@ func main() {
3939
}
4040
}
4141

42-
func findGit(envPath string) string {
42+
func FindGit(envPath string) string {
4343
paths := strings.Split(envPath, string(os.PathListSeparator))
4444
var shimPath string
4545

@@ -66,16 +66,26 @@ func findGit(envPath string) string {
6666
return ""
6767
}
6868

69-
var sshUrl = regexp.MustCompile(`^(?P<user>.*?)@(?P<host>.*?):(?:(?P<port>.*?)/)?(?P<path>.*?/.*?)$`)
69+
var scpUrl = regexp.MustCompile(`^(?P<user>\S+?)@(?P<host>[a-zA-Z\d-]+(\.[a-zA-Z\d-]+)+\.?):(?P<path>.*?/.*?)$`)
7070

7171
func Scrub(argument string) string {
72-
u, err := url.Parse(argument)
72+
u, err := url.ParseRequestURI(argument)
7373
if err == nil && u.Scheme != "" {
7474
u.Scheme = "https"
7575
return u.String()
7676
}
77-
if sshUrl.MatchString(argument) {
78-
return "https://" + strings.Replace(argument, ":", "/", 1)
77+
if scpUrl.MatchString(argument) {
78+
newUrl := "https://" + strings.Replace(argument, ":", "/", 1)
79+
u, err = url.ParseRequestURI(newUrl)
80+
if err != nil {
81+
// new URI isn't valid
82+
return argument
83+
}
84+
if u.Host != scpUrl.FindStringSubmatch(argument)[2] {
85+
// host changed, possible attack
86+
return argument
87+
}
88+
return newUrl
7989
}
8090
return argument
8191
}

main_test.go

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package main
22

33
import (
44
"fmt"
5+
"net/url"
56
"testing"
67
)
78

@@ -43,13 +44,50 @@ func TestScrub(t *testing.T) {
4344

4445
func TestFindGit(t *testing.T) {
4546
t.Run("finds the second git", func(t *testing.T) {
46-
if v := findGit("test/bin1:test/bin2:test/bin3"); v != "test/bin2/git" {
47+
if v := FindGit("test/bin1:test/bin2:test/bin3"); v != "test/bin2/git" {
4748
t.Errorf(v)
4849
}
4950
})
5051
t.Run("finds nothing", func(t *testing.T) {
51-
if v := findGit("test/bin1"); v != "" {
52+
if v := FindGit("test/bin1"); v != "" {
5253
t.Errorf(v)
5354
}
5455
})
5556
}
57+
58+
func FuzzScrub(f *testing.F) {
59+
testcases := []string{
60+
"https://github.com/org/repo",
61+
"ssh://[email protected]/org/repo",
62+
"[email protected]:org/repo",
63+
"git://github.com/repo",
64+
"HEAD~1",
65+
"+/refs/HEAD",
66+
}
67+
for _, tc := range testcases {
68+
f.Add(tc)
69+
}
70+
f.Fuzz(func(t *testing.T, orig string) {
71+
result := Scrub(orig)
72+
if result != orig {
73+
if extractHost(orig, true) != extractHost(result, false) {
74+
// transformed a nonURL into a URL or changed what the URL was, which could be an attack
75+
t.Errorf("Before: %q (%q), after: %q (%q)", orig, extractHost(orig, true), result, extractHost(result, false))
76+
}
77+
}
78+
})
79+
}
80+
81+
func extractHost(input string, orig bool) string {
82+
u, err := url.ParseRequestURI(input)
83+
if err == nil {
84+
return u.Hostname()
85+
}
86+
if !orig {
87+
return ""
88+
}
89+
if scpUrl.MatchString(input) {
90+
return scpUrl.FindStringSubmatch(input)[2]
91+
}
92+
return ""
93+
}

0 commit comments

Comments
 (0)