Skip to content

Commit b74076c

Browse files
authored
Merge pull request #1184 from scop/feat/ssh-keygen-opts
feat(ssh-keygen): -O arg improvements
2 parents 43beafc + b71340b commit b74076c

File tree

2 files changed

+83
-31
lines changed

2 files changed

+83
-31
lines changed

completions/ssh-keygen

Lines changed: 63 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@ _comp_cmd_ssh_keygen()
55
local cur prev words cword comp_args
66
_comp_initialize -n := -- "$@" || return
77

8+
local IFS=$' \t\n' # for ${words[*]}
9+
local noargopts='!(-*|*[ aCIJjMNPSVWzbEFRDwfGKsTmnOrtY]*)'
10+
# shellcheck disable=SC2254
811
case $prev in
9-
-*[aCIJjMNPSVWz])
12+
-${noargopts}[aCIJjMNPSVWz])
1013
return
1114
;;
12-
-*b)
15+
-${noargopts}b)
1316
local -a sizes=()
1417
case "${words[*]}" in
1518
*" -t dsa"?( *))
@@ -26,28 +29,28 @@ _comp_cmd_ssh_keygen()
2629
_comp_compgen -- -W '"${sizes[@]}"'
2730
return
2831
;;
29-
-*E)
32+
-${noargopts}E)
3033
_comp_compgen -- -W 'md5 sha256'
3134
return
3235
;;
33-
-*[FR])
36+
-${noargopts}[FR])
3437
# TODO: trim this down to actual entries in known hosts files
3538
_comp_compgen_known_hosts -- "$cur"
3639
return
3740
;;
38-
-*[Dw])
41+
-${noargopts}[Dw])
3942
_comp_compgen_filedir so
4043
return
4144
;;
42-
-*[fGKsT])
45+
-${noargopts}[fGKsT])
4346
_comp_compgen_filedir
4447
return
4548
;;
46-
-*m)
49+
-${noargopts}m)
4750
_comp_compgen -- -W 'PEM PKCS8 RFC4716'
4851
return
4952
;;
50-
-*n)
53+
-${noargopts}n)
5154
[[ ${words[*]} != *\ -*Y\ * ]] || return
5255
if [[ ${words[*]} == *\ -*h\ * ]]; then
5356
_comp_compgen_known_hosts -- "${cur##*,}"
@@ -58,19 +61,41 @@ _comp_cmd_ssh_keygen()
5861
fi
5962
return
6063
;;
61-
-*O)
64+
-${noargopts}O)
6265
if [[ $cur != *=* ]]; then
63-
_comp_compgen -- -W 'clear critical: extension: force-command=
64-
no-agent-forwarding no-port-forwarding no-pty no-user-rc
65-
no-x11-forwarding permit-agent-forwarding
66-
permit-port-forwarding permit-pty permit-user-rc
67-
permit-X11-forwarding no-touch-required source-address=
68-
verify-required
69-
70-
lines= start-line= checkpoint= memory= start= generator=
71-
72-
application= challenge= device= no-touch-required resident
73-
user= write-attestation='
66+
local -a opts=()
67+
case ${words[*]} in
68+
*\ -${noargopts}M\ *)
69+
opts=(
70+
lines= start-line= checkpoint= memory= start=
71+
generator=
72+
)
73+
;;
74+
*\ -${noargopts}r\ *)
75+
opts=(hashalg=)
76+
;;
77+
*\ -${noargopts}s\ *)
78+
opts=(
79+
clear critical: extension: force-command=
80+
no-agent-forwarding no-port-forwarding no-pty
81+
no-user-rc no-x11-forwarding permit-agent-forwarding
82+
permit-port-forwarding permit-pty permit-user-rc
83+
permit-X11-forwarding no-touch-required
84+
source-address= verify-required
85+
)
86+
;;
87+
*\ -${noargopts}t\ +([a-z0-9])-sk\ *)
88+
opts=(
89+
application= challenge= device= no-touch-required
90+
resident user= verify-required write-attestation=
91+
)
92+
;;
93+
*\ -${noargopts}Y\ *)
94+
opts=(hashalg= print-pubkey verify-time)
95+
;;
96+
esac
97+
((${#opts[@]})) &&
98+
_comp_compgen -- -W '"${opts[@]}"'
7499

75100
[[ ${COMPREPLY-} == *[:=] ]] && compopt -o nospace
76101
_comp_ltrim_colon_completions "$cur"
@@ -90,15 +115,28 @@ _comp_cmd_ssh_keygen()
90115
user=*)
91116
_comp_compgen -c "${cur#*=}" -- -u
92117
;;
118+
hashalg=*)
119+
local -a args=()
120+
case ${words[*]} in
121+
*\ -*Y\ *)
122+
args=(sha256 sha512)
123+
;;
124+
*\ -*r\ *)
125+
args=(sha1 sha256)
126+
;;
127+
esac
128+
((${#args[@]})) &&
129+
_comp_compgen -c "${cur#*=}" -- -W '"${args[@]}"'
130+
;;
93131
esac
94132
fi
95133
return
96134
;;
97-
-*r)
98-
[[ ${words[*]} != *\ -*Y\ * ]] || _comp_compgen_filedir
135+
-${noargopts}r)
136+
[[ ${words[*]} != *\ -${noargopts}Y\ * ]] || _comp_compgen_filedir
99137
return
100138
;;
101-
-*t)
139+
-${noargopts}t)
102140
# Prefer `ssh` from same dir for resolving options, etc
103141
local pathcmd protocols
104142
pathcmd=$(type -P -- "$1") && local PATH=${pathcmd%/*}:$PATH
@@ -110,7 +148,7 @@ _comp_cmd_ssh_keygen()
110148
_comp_compgen -- -W "$types"
111149
return
112150
;;
113-
-*Y)
151+
-${noargopts}Y)
114152
_comp_compgen -- -W 'find-principals check-novalidate sign verify'
115153
return
116154
;;
@@ -122,7 +160,7 @@ _comp_cmd_ssh_keygen()
122160
_comp_compgen_help -- "-?" # OpenSSH < 7
123161
fi
124162

125-
if [[ ${words[*]} == *\ -*s\ * ]]; then
163+
if [[ ${words[*]} == *\ -${noargopts}s\ * ]]; then
126164
_comp_compgen -a filedir pub
127165
fi
128166
} &&

test/t/test_ssh_keygen.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,15 @@ def test_ecdsa_sk_b(self, completion):
5555
assert not completion
5656

5757
@pytest.mark.complete("ssh-keygen -O ")
58-
def test_O(self, completion):
58+
def test_bare_O(self, completion):
59+
assert not completion
60+
61+
@pytest.mark.complete("ssh-keygen -s -O ")
62+
def test_s_O(self, completion):
5963
assert completion
6064
assert any(x.endswith("=") for x in completion)
6165

62-
@pytest.mark.complete("ssh-keygen -O force-command=bas")
66+
@pytest.mark.complete("ssh-keygen -s -O force-command=bas")
6367
def test_O_force_command(self, completion):
6468
assert completion
6569
assert not completion.startswith("force-command=")
@@ -68,18 +72,28 @@ def test_O_force_command(self, completion):
6872
def test_O_unknown(self, completion):
6973
assert not completion
7074

71-
@pytest.mark.complete("ssh-keygen -O application=")
75+
@pytest.mark.complete("ssh-keygen -t ed25519-sk -O application=")
7276
def test_O_application(self, completion):
7377
assert completion == "ssh:"
7478

75-
@pytest.mark.complete("ssh-keygen -O application=s")
79+
@pytest.mark.complete("ssh-keygen -t ed25519-sk -O application=s")
7680
def test_O_application_s(self, completion):
7781
assert completion == "sh:"
7882

79-
@pytest.mark.complete("ssh-keygen -O application=ssh:")
83+
@pytest.mark.complete("ssh-keygen -t ed25519-sk -O application=ssh:")
8084
def test_O_application_ssh_colon(self, completion):
8185
assert not completion
8286

83-
@pytest.mark.complete("ssh-keygen -O application=nonexistent")
87+
@pytest.mark.complete(
88+
"ssh-keygen -t ed25519-sk -O application=nonexistent"
89+
)
8490
def test_O_application_nonexistent(self, completion):
8591
assert not completion
92+
93+
@pytest.mark.complete("ssh-keygen -r -O ")
94+
def test_r_O(self, completion):
95+
assert completion
96+
97+
@pytest.mark.complete("ssh-keygen -Y -O ")
98+
def test_Y_O(self, completion):
99+
assert completion

0 commit comments

Comments
 (0)