blob: 589f58d762910a79247b4907557b3251d83d929b (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
package commandargs
import (
"errors"
"regexp"
"github.com/mattn/go-shellwords"
"gitlab.com/gitlab-org/gitlab-shell/internal/sshenv"
)
const (
Discover CommandType = "discover"
TwoFactorRecover CommandType = "2fa_recovery_codes"
TwoFactorVerify CommandType = "2fa_verify"
LfsAuthenticate CommandType = "git-lfs-authenticate"
ReceivePack CommandType = "git-receive-pack"
UploadPack CommandType = "git-upload-pack"
UploadArchive CommandType = "git-upload-archive"
PersonalAccessToken CommandType = "personal_access_token"
)
var (
whoKeyRegex = regexp.MustCompile(`\Akey-(?P<keyid>\d+)\z`)
whoUsernameRegex = regexp.MustCompile(`\Ausername-(?P<username>\S+)\z`)
)
type Shell struct {
Arguments []string
GitlabUsername string
GitlabKeyId string
SshArgs []string
CommandType CommandType
Env sshenv.Env
}
func (s *Shell) Parse() error {
if err := s.validate(); err != nil {
return err
}
s.parseWho()
return nil
}
func (s *Shell) GetArguments() []string {
return s.Arguments
}
func (s *Shell) validate() error {
if !s.Env.IsSSHConnection {
return errors.New("Only SSH allowed")
}
if !s.isValidSSHCommand() {
return errors.New("Invalid SSH command")
}
return nil
}
func (s *Shell) isValidSSHCommand() bool {
err := s.ParseCommand(s.Env.OriginalCommand)
return err == 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
s.defineCommandType()
return nil
}
func (s *Shell) defineCommandType() {
if len(s.SshArgs) == 0 {
s.CommandType = Discover
} else {
s.CommandType = CommandType(s.SshArgs[0])
}
}
|