From 04267602115b17e683b22bc0c8fe34e65914681a Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Sat, 1 Jun 2019 20:59:40 -0600 Subject: [PATCH] preliminary git-proxy --- git-proxy/go.mod | 3 ++ git-proxy/parseargs.go | 59 +++++++++++++++++++++++++++++++++++++ git-proxy/parseargs_test.go | 45 ++++++++++++++++++++++++++++ git-proxy/proxy.go | 43 +++++++++++++++++++++++++++ 4 files changed, 150 insertions(+) create mode 100644 git-proxy/go.mod create mode 100644 git-proxy/parseargs.go create mode 100644 git-proxy/parseargs_test.go create mode 100644 git-proxy/proxy.go diff --git a/git-proxy/go.mod b/git-proxy/go.mod new file mode 100644 index 0000000..512eab2 --- /dev/null +++ b/git-proxy/go.mod @@ -0,0 +1,3 @@ +module git.coolaj86.com/coolaj86/git-scripts/git-proxy + +go 1.12 diff --git a/git-proxy/parseargs.go b/git-proxy/parseargs.go new file mode 100644 index 0000000..0050b7a --- /dev/null +++ b/git-proxy/parseargs.go @@ -0,0 +1,59 @@ +package main + +import ( + "fmt" + "strings" + "unicode" +) + +const NullStr = rune(0) + +// ParseArgs will parse a string that contains quoted strings the same as bash does +// (same as most other *nix shells do). This is secure in the sense that it doesn't do any +// executing or interpeting. However, it also doesn't do any escaping, so you shouldn't pass +// these strings to shells without escaping them. +func ParseArgs(str string) ([]string, error) { + var m []string + var s string + + str = strings.TrimSpace(str) + " " + + lastQuote := NullStr + isSpace := false + for i, c := range str { + switch { + // If we're ending a quote, break out and skip this character + case c == lastQuote: + lastQuote = NullStr + + // If we're in a quote, count this character + case lastQuote != NullStr: + s += string(c) + + // If we encounter a quote, enter it and skip this character + case unicode.In(c, unicode.Quotation_Mark): + isSpace = false + lastQuote = c + + // If it's a space, store the string + case unicode.IsSpace(c): + if 0 == i || isSpace { + continue + } + isSpace = true + m = append(m, s) + s = "" + + default: + isSpace = false + s += string(c) + } + + } + + if lastQuote != NullStr { + return nil, fmt.Errorf("Quotes did not terminate") + } + + return m, nil +} diff --git a/git-proxy/parseargs_test.go b/git-proxy/parseargs_test.go new file mode 100644 index 0000000..c6c13cd --- /dev/null +++ b/git-proxy/parseargs_test.go @@ -0,0 +1,45 @@ +package main + +import ( + "fmt" + "log" + "strings" + "testing" +) + +func TestStrings(t *testing.T) { + tests := [][]string{ + []string{ + ` a 'b' '' '"d" e' " f " ""''""''''"""" g"h"'i'jkl'mno'pqr $("dangerous dangerous danger-ous-ous-ous")`, + "a", "b", "", `"d" e`, " f ", "", "ghijklmnopqr", "$(dangerous dangerous danger-ous-ous-ous)", + }, + []string{ + ` arg1 arg2 ' hello world'"'" '' " ' another hello world ' " `, + "arg1", "arg2", " hello world'", "", " ' another hello world ' ", + }, + []string{ + `arg1 arg2 ' hello world'"'" " "" ' another hello world ' "`, + "arg1", "arg2", " hello world'", " ' another hello world ' ", + }, + []string{ + ` arg1 arg2 ' hello world'"'" "" " ' another hello world ' "`, + "arg1", "arg2", " hello world'", "", " ' another hello world ' ", + }, + []string{ + `arg1 arg2 ' hello world'"'" "" " ' another hello world ' `, + }, + } + + for i := range tests { + strs := tests[i] + in := strs[0] + expected := strs[1:] + + actual, _ := ParseArgs(in) + if strings.Join(actual, "#") != strings.Join(expected, "#") { + fmt.Printf("Expected: %#v\n", expected) + fmt.Printf("Actual: %#v\n", actual) + log.Fatal("Test failed.") + } + } +} diff --git a/git-proxy/proxy.go b/git-proxy/proxy.go new file mode 100644 index 0000000..ae99f4f --- /dev/null +++ b/git-proxy/proxy.go @@ -0,0 +1,43 @@ +package main + +import ( + "errors" + "fmt" + "log" + "os" + "os/exec" +) + +var ErrGitOnlyShell = errors.New("You've successfully authenticated, but this is a git-only shell") +var gitcmds = map[string]bool{ + "git-receive-pack": true, + "git-upload-pack": true, + "git-upload-archive": true, +} + +func main() { + cmds, err := ParseArgs(os.Getenv("SSH_ORIGINAL_COMMAND")) + if nil != err { + fmt.Fprintf(os.Stderr, "%s\n", err) + os.Exit(1) + } + + if len(cmds) < 2 { + fmt.Fprintf(os.Stderr, "%s\n", ErrGitOnlyShell) + os.Exit(1) + } + + bin := cmds[0] + _, ok := gitcmds[bin] + if !ok { + fmt.Fprintf(os.Stderr, "%s\n", ErrGitOnlyShell) + os.Exit(1) + } + + args := cmds[1:] + cmd := exec.Command(bin, args...) + cmd.Env = append(os.Environ(), "GIT_PROXY=true") + if err := cmd.Run(); err != nil { + log.Fatal(err) + } +}