|
2 | 2 |
|
3 | 3 | package cli
|
4 | 4 |
|
| 5 | +import ( |
| 6 | + "strings" |
| 7 | + |
| 8 | + "golang.org/x/xerrors" |
| 9 | +) |
| 10 | + |
5 | 11 | var hideForceUnixSlashes = true
|
| 12 | + |
| 13 | +// sshConfigMatchExecEscape prepares the path for use in `Match exec` statement. |
| 14 | +// |
| 15 | +// OpenSSH parses the Match line with a very simple tokenizer that accepts "-enclosed strings for the exec command, and |
| 16 | +// has no supported escape sequences for ". This means we cannot include " within the command to execute. |
| 17 | +func sshConfigMatchExecEscape(path string) (string, error) { |
| 18 | + // This is unlikely to ever happen, but newlines are allowed on |
| 19 | + // certain filesystems, but cannot be used inside ssh config. |
| 20 | + if strings.ContainsAny(path, "\n") { |
| 21 | + return "", xerrors.Errorf("invalid path: %s", path) |
| 22 | + } |
| 23 | + // Quotes are allowed in path names on unix-like file systems, but OpenSSH's parsing of `Match exec` doesn't allow |
| 24 | + // them. |
| 25 | + if strings.Contains(path, `"`) { |
| 26 | + return "", xerrors.Errorf("path must not contain quotes: %q", path) |
| 27 | + } |
| 28 | + |
| 29 | + // OpenSSH passes the match exec string directly to the user's shell. sh, bash and zsh accept spaces and tabs |
| 30 | + // simply escaped by a `\`. It's hard to predict exactly what more exotic shells might do, but this should work for |
| 31 | + // macOS and most Linux distros in their default configuration. |
| 32 | + path = strings.ReplaceAll(path, " ", "\\ ") |
| 33 | + path = strings.ReplaceAll(path, "\t", "\\\t") |
| 34 | + return path, nil |
| 35 | +} |
0 commit comments