From 3b0176df497263323da2fae793a79b568502e6db Mon Sep 17 00:00:00 2001 From: Patrick Bajao Date: Mon, 29 Jul 2019 15:56:00 +0800 Subject: Support different CommandArgs type `CommandArgs` has been renamed to `Shell`. An interface has been added that includes `Executable()` and `Arguments()` method. The `BaseArgs` implement this methods and should be embeeded in each type. --- go/internal/command/commandargs/shell.go | 110 +++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 go/internal/command/commandargs/shell.go (limited to 'go/internal/command/commandargs/shell.go') diff --git a/go/internal/command/commandargs/shell.go b/go/internal/command/commandargs/shell.go new file mode 100644 index 0000000..04b1040 --- /dev/null +++ b/go/internal/command/commandargs/shell.go @@ -0,0 +1,110 @@ +package commandargs + +import ( + "errors" + "os" + "regexp" + + "github.com/mattn/go-shellwords" +) + +const ( + Discover CommandType = "discover" + TwoFactorRecover CommandType = "2fa_recovery_codes" + LfsAuthenticate CommandType = "git-lfs-authenticate" + ReceivePack CommandType = "git-receive-pack" + UploadPack CommandType = "git-upload-pack" + UploadArchive CommandType = "git-upload-archive" +) + +var ( + whoKeyRegex = regexp.MustCompile(`\bkey-(?P\d+)\b`) + whoUsernameRegex = regexp.MustCompile(`\busername-(?P\S+)\b`) +) + +type Shell struct { + *BaseArgs + GitlabUsername string + GitlabKeyId string + SshArgs []string + CommandType CommandType +} + +func (s *Shell) Parse() error { + if sshConnection := os.Getenv("SSH_CONNECTION"); sshConnection == "" { + return errors.New("Only ssh allowed") + } + + s.parseWho() + + if err := s.parseCommand(os.Getenv("SSH_ORIGINAL_COMMAND")); err != nil { + return errors.New("Invalid ssh command") + } + + s.defineCommandType() + + return nil +} + +func (s *Shell) parseWho() { + for _, argument := range s.arguments { + if keyId := tryParseKeyId(argument); keyId != "" { + s.GitlabKeyId = keyId + break + } + + if username := tryParseUsername(argument); username != "" { + s.GitlabUsername = username + break + } + } +} + +func tryParseKeyId(argument string) string { + matchInfo := whoKeyRegex.FindStringSubmatch(argument) + if len(matchInfo) == 2 { + // The first element is the full matched string + // The second element is the named `keyid` + return matchInfo[1] + } + + return "" +} + +func tryParseUsername(argument string) string { + matchInfo := whoUsernameRegex.FindStringSubmatch(argument) + if len(matchInfo) == 2 { + // The first element is the full matched string + // The second element is the named `username` + return matchInfo[1] + } + + return "" +} + +func (s *Shell) parseCommand(commandString string) error { + args, err := shellwords.Parse(commandString) + if err != nil { + return err + } + + // Handle Git for Windows 2.14 using "git upload-pack" instead of git-upload-pack + if len(args) > 1 && args[0] == "git" { + command := args[0] + "-" + args[1] + commandArgs := args[2:] + + args = append([]string{command}, commandArgs...) + } + + s.SshArgs = args + + return nil +} + +func (s *Shell) defineCommandType() { + if len(s.SshArgs) == 0 { + s.CommandType = Discover + } else { + s.CommandType = CommandType(s.SshArgs[0]) + } +} -- cgit v1.2.1 From 3b6f9f7583755e041e76142d7caf7716937907fa Mon Sep 17 00:00:00 2001 From: Patrick Bajao Date: Fri, 2 Aug 2019 16:10:17 +0800 Subject: Add Executable struct This struct is responsible for determining the name and root dir of the executable. The `RootDir` property will be used to find the config. The `Name` property will be used to determine what `Command` and `CommandArgs` to be built. --- go/internal/command/commandargs/shell.go | 35 +++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) (limited to 'go/internal/command/commandargs/shell.go') diff --git a/go/internal/command/commandargs/shell.go b/go/internal/command/commandargs/shell.go index 04b1040..7e2b72e 100644 --- a/go/internal/command/commandargs/shell.go +++ b/go/internal/command/commandargs/shell.go @@ -23,7 +23,7 @@ var ( ) type Shell struct { - *BaseArgs + Arguments []string GitlabUsername string GitlabKeyId string SshArgs []string @@ -31,23 +31,44 @@ type Shell struct { } func (s *Shell) Parse() error { - if sshConnection := os.Getenv("SSH_CONNECTION"); sshConnection == "" { - return errors.New("Only ssh allowed") + if err := s.validate(); err != nil { + return err } s.parseWho() + s.defineCommandType() + + return nil +} + +func (s *Shell) GetArguments() []string { + return s.Arguments +} - if err := s.parseCommand(os.Getenv("SSH_ORIGINAL_COMMAND")); err != nil { - return errors.New("Invalid ssh command") +func (s *Shell) validate() error { + if !s.isSshConnection() { + return errors.New("Only SSH allowed") } - s.defineCommandType() + if !s.isValidSshCommand() { + return errors.New("Invalid SSH command") + } return nil } +func (s *Shell) isSshConnection() bool { + ok := os.Getenv("SSH_CONNECTION") + return ok != "" +} + +func (s *Shell) isValidSshCommand() bool { + err := s.parseCommand(os.Getenv("SSH_ORIGINAL_COMMAND")) + return err == nil +} + func (s *Shell) parseWho() { - for _, argument := range s.arguments { + for _, argument := range s.Arguments { if keyId := tryParseKeyId(argument); keyId != "" { s.GitlabKeyId = keyId break -- cgit v1.2.1