diff --git a/command/ssh.go b/command/ssh.go index 986228d525ec..40307043a68b 100644 --- a/command/ssh.go +++ b/command/ssh.go @@ -761,12 +761,30 @@ func (c *SSHCommand) defaultRole(mountPoint, ip string) (string, error) { } } +func (c *SSHCommand) isSingleSSHArg(arg string) bool { + // list of single SSH arguments is taken from + // https://github.com/openssh/openssh-portable/blob/28013759f09ed3ebf7e8335e83a62936bd7a7f47/ssh.c#L204 + singleArgs := []string{ + "4", "6", "A", "a", "C", "f", "G", "g", "K", "k", "M", "N", "n", "q", + "s", "T", "t", "V", "v", "X", "x", "Y", "y", + } + + // We want to get the first character after the dash. This is so args like -vvv are picked up as just being -v + flag := string(arg[1]) + + for _, a := range singleArgs { + if flag == a { + return true + } + } + return false +} + // Finds the hostname, username (optional) and port (optional) from any valid ssh command // Supports usrname@hostname but also specifying valid ssh flags like -o User=username, // -o Port=2222 and -p 2222 anywhere in the command func (c *SSHCommand) parseSSHCommand(args []string) (hostname string, username string, port string, err error) { lastArg := "" - for _, i := range args { arg := lastArg lastArg = "" @@ -807,9 +825,12 @@ func (c *SSHCommand) parseSSHCommand(args []string) (hostname string, username s continue } - // If this is an ssh argument we want to look at the value + // If this is an ssh argument with a value we want to look at it in the next loop if strings.HasPrefix(i, "-") { - lastArg = i + // If this isn't a single SSH arg we want to store the flag to we can look at the value next loop + if !c.isSingleSSHArg(i) { + lastArg = i + } continue } diff --git a/command/ssh_test.go b/command/ssh_test.go index 3ed4a9ea93b1..d6f1465059e8 100644 --- a/command/ssh_test.go +++ b/command/ssh_test.go @@ -133,6 +133,31 @@ func TestParseSSHCommand(t *testing.T) { "", nil, }, + { + "Allow single args which don't have a value", + []string{ + "-v", + "hostname", + }, + "hostname", + "", + "", + nil, + }, + { + "Allow single args before and after the hostname and command", + []string{ + "-v", + "hostname", + "-v", + "command", + "-v", + }, + "hostname", + "", + "", + nil, + }, } for _, test := range tests { @@ -154,3 +179,39 @@ func TestParseSSHCommand(t *testing.T) { }) } } + +func TestIsSingleSSHArg(t *testing.T) { + t.Parallel() + + _, cmd := testSSHCommand(t) + var tests = []struct { + name string + arg string + want bool + }{ + { + "-v is a single ssh arg", + "-v", + true, + }, + { + "-o is NOT a single ssh arg", + "-o", + false, + }, + { + "Repeated args like -vvv is still a single ssh arg", + "-vvv", + true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got := cmd.isSingleSSHArg(test.arg) + if got != test.want { + t.Errorf("arg %q got %v want %v", test.arg, got, test.want) + } + }) + } +}