]> git.ipfire.org Git - thirdparty/git.git/blob - t/t5534-push-signed.sh
The third batch
[thirdparty/git.git] / t / t5534-push-signed.sh
1 #!/bin/sh
2
3 test_description='signed push'
4
5 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
6 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
7
8 . ./test-lib.sh
9 . "$TEST_DIRECTORY"/lib-gpg.sh
10
11 prepare_dst () {
12 rm -fr dst &&
13 test_create_repo dst &&
14
15 git push dst main:noop main:ff main:noff
16 }
17
18 test_expect_success setup '
19 # main, ff and noff branches pointing at the same commit
20 test_tick &&
21 git commit --allow-empty -m initial &&
22
23 git checkout -b noop &&
24 git checkout -b ff &&
25 git checkout -b noff &&
26
27 # noop stays the same, ff advances, noff rewrites
28 test_tick &&
29 git commit --allow-empty --amend -m rewritten &&
30 git checkout ff &&
31
32 test_tick &&
33 git commit --allow-empty -m second
34 '
35
36 test_expect_success 'unsigned push does not send push certificate' '
37 prepare_dst &&
38 test_hook -C dst post-receive <<-\EOF &&
39 # discard the update list
40 cat >/dev/null
41 # record the push certificate
42 if test -n "${GIT_PUSH_CERT-}"
43 then
44 git cat-file blob $GIT_PUSH_CERT >../push-cert
45 fi
46 EOF
47
48 git push dst noop ff +noff &&
49 ! test -f dst/push-cert
50 '
51
52 test_expect_success 'talking with a receiver without push certificate support' '
53 prepare_dst &&
54 test_hook -C dst post-receive <<-\EOF &&
55 # discard the update list
56 cat >/dev/null
57 # record the push certificate
58 if test -n "${GIT_PUSH_CERT-}"
59 then
60 git cat-file blob $GIT_PUSH_CERT >../push-cert
61 fi
62 EOF
63
64 git push dst noop ff +noff &&
65 ! test -f dst/push-cert
66 '
67
68 test_expect_success 'push --signed fails with a receiver without push certificate support' '
69 prepare_dst &&
70 test_must_fail git push --signed dst noop ff +noff 2>err &&
71 test_grep "the receiving end does not support" err
72 '
73
74 test_expect_success 'push --signed=1 is accepted' '
75 prepare_dst &&
76 test_must_fail git push --signed=1 dst noop ff +noff 2>err &&
77 test_grep "the receiving end does not support" err
78 '
79
80 test_expect_success GPG 'no certificate for a signed push with no update' '
81 prepare_dst &&
82 test_hook -C dst post-receive <<-\EOF &&
83 if test -n "${GIT_PUSH_CERT-}"
84 then
85 git cat-file blob $GIT_PUSH_CERT >../push-cert
86 fi
87 EOF
88 git push dst noop &&
89 ! test -f dst/push-cert
90 '
91
92 test_expect_success GPG 'signed push sends push certificate' '
93 prepare_dst &&
94 git -C dst config receive.certnonceseed sekrit &&
95 test_hook -C dst post-receive <<-\EOF &&
96 # discard the update list
97 cat >/dev/null
98 # record the push certificate
99 if test -n "${GIT_PUSH_CERT-}"
100 then
101 git cat-file blob $GIT_PUSH_CERT >../push-cert
102 fi &&
103
104 cat >../push-cert-status <<E_O_F
105 SIGNER=${GIT_PUSH_CERT_SIGNER-nobody}
106 KEY=${GIT_PUSH_CERT_KEY-nokey}
107 STATUS=${GIT_PUSH_CERT_STATUS-nostatus}
108 NONCE_STATUS=${GIT_PUSH_CERT_NONCE_STATUS-nononcestatus}
109 NONCE=${GIT_PUSH_CERT_NONCE-nononce}
110 E_O_F
111
112 EOF
113
114 git push --signed dst noop ff +noff &&
115
116 (
117 cat <<-\EOF &&
118 SIGNER=C O Mitter <committer@example.com>
119 KEY=13B6F51ECDDE430D
120 STATUS=G
121 NONCE_STATUS=OK
122 EOF
123 sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert
124 ) >expect &&
125
126 noop=$(git rev-parse noop) &&
127 ff=$(git rev-parse ff) &&
128 noff=$(git rev-parse noff) &&
129 grep "$noop $ff refs/heads/ff" dst/push-cert &&
130 grep "$noop $noff refs/heads/noff" dst/push-cert &&
131 test_cmp expect dst/push-cert-status
132 '
133
134 test_expect_success GPGSSH 'ssh signed push sends push certificate' '
135 prepare_dst &&
136 git -C dst config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
137 git -C dst config receive.certnonceseed sekrit &&
138 test_hook -C dst post-receive <<-\EOF &&
139 # discard the update list
140 cat >/dev/null
141 # record the push certificate
142 if test -n "${GIT_PUSH_CERT-}"
143 then
144 git cat-file blob $GIT_PUSH_CERT >../push-cert
145 fi &&
146
147 cat >../push-cert-status <<E_O_F
148 SIGNER=${GIT_PUSH_CERT_SIGNER-nobody}
149 KEY=${GIT_PUSH_CERT_KEY-nokey}
150 STATUS=${GIT_PUSH_CERT_STATUS-nostatus}
151 NONCE_STATUS=${GIT_PUSH_CERT_NONCE_STATUS-nononcestatus}
152 NONCE=${GIT_PUSH_CERT_NONCE-nononce}
153 E_O_F
154
155 EOF
156
157 test_config gpg.format ssh &&
158 test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
159 FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_PRIMARY}" | awk "{print \$2;}") &&
160 git push --signed dst noop ff +noff &&
161
162 (
163 cat <<-\EOF &&
164 SIGNER=principal with number 1
165 KEY=FINGERPRINT
166 STATUS=G
167 NONCE_STATUS=OK
168 EOF
169 sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert
170 ) | sed -e "s|FINGERPRINT|$FINGERPRINT|" >expect &&
171
172 noop=$(git rev-parse noop) &&
173 ff=$(git rev-parse ff) &&
174 noff=$(git rev-parse noff) &&
175 grep "$noop $ff refs/heads/ff" dst/push-cert &&
176 grep "$noop $noff refs/heads/noff" dst/push-cert &&
177 test_cmp expect dst/push-cert-status
178 '
179
180 test_expect_success GPG 'inconsistent push options in signed push not allowed' '
181 # First, invoke receive-pack with dummy input to obtain its preamble.
182 prepare_dst &&
183 git -C dst config receive.certnonceseed sekrit &&
184 git -C dst config receive.advertisepushoptions 1 &&
185 printf xxxx | test_might_fail git receive-pack dst >preamble &&
186
187 # Then, invoke push. Simulate a receive-pack that sends the preamble we
188 # obtained, followed by a dummy packet.
189 write_script myscript <<-\EOF &&
190 cat preamble &&
191 printf xxxx &&
192 cat >push
193 EOF
194 test_might_fail git push --push-option="foo" --push-option="bar" \
195 --receive-pack="\"$(pwd)/myscript\"" --signed dst --delete ff &&
196
197 # Replay the push output on a fresh dst, checking that ff is truly
198 # deleted.
199 prepare_dst &&
200 git -C dst config receive.certnonceseed sekrit &&
201 git -C dst config receive.advertisepushoptions 1 &&
202 git receive-pack dst <push &&
203 test_must_fail git -C dst rev-parse ff &&
204
205 # Tweak the push output to make the push option outside the cert
206 # different, then replay it on a fresh dst, checking that ff is not
207 # deleted.
208 perl -pe "s/([^ ])bar/\$1baz/" push >push.tweak &&
209 prepare_dst &&
210 git -C dst config receive.certnonceseed sekrit &&
211 git -C dst config receive.advertisepushoptions 1 &&
212 git receive-pack dst <push.tweak >out &&
213 git -C dst rev-parse ff &&
214 grep "inconsistent push options" out
215 '
216
217 test_expect_success GPG 'fail without key and heed user.signingkey' '
218 prepare_dst &&
219 git -C dst config receive.certnonceseed sekrit &&
220 test_hook -C dst post-receive <<-\EOF &&
221 # discard the update list
222 cat >/dev/null
223 # record the push certificate
224 if test -n "${GIT_PUSH_CERT-}"
225 then
226 git cat-file blob $GIT_PUSH_CERT >../push-cert
227 fi &&
228
229 cat >../push-cert-status <<E_O_F
230 SIGNER=${GIT_PUSH_CERT_SIGNER-nobody}
231 KEY=${GIT_PUSH_CERT_KEY-nokey}
232 STATUS=${GIT_PUSH_CERT_STATUS-nostatus}
233 NONCE_STATUS=${GIT_PUSH_CERT_NONCE_STATUS-nononcestatus}
234 NONCE=${GIT_PUSH_CERT_NONCE-nononce}
235 E_O_F
236
237 EOF
238
239 test_config user.email hasnokey@nowhere.com &&
240 (
241 sane_unset GIT_COMMITTER_EMAIL &&
242 test_must_fail git push --signed dst noop ff +noff
243 ) &&
244 test_config user.signingkey $GIT_COMMITTER_EMAIL &&
245 git push --signed dst noop ff +noff &&
246
247 (
248 cat <<-\EOF &&
249 SIGNER=C O Mitter <committer@example.com>
250 KEY=13B6F51ECDDE430D
251 STATUS=G
252 NONCE_STATUS=OK
253 EOF
254 sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert
255 ) >expect &&
256
257 noop=$(git rev-parse noop) &&
258 ff=$(git rev-parse ff) &&
259 noff=$(git rev-parse noff) &&
260 grep "$noop $ff refs/heads/ff" dst/push-cert &&
261 grep "$noop $noff refs/heads/noff" dst/push-cert &&
262 test_cmp expect dst/push-cert-status
263 '
264
265 test_expect_success GPGSM 'fail without key and heed user.signingkey x509' '
266 test_config gpg.format x509 &&
267 prepare_dst &&
268 git -C dst config receive.certnonceseed sekrit &&
269 test_hook -C dst post-receive <<-\EOF &&
270 # discard the update list
271 cat >/dev/null
272 # record the push certificate
273 if test -n "${GIT_PUSH_CERT-}"
274 then
275 git cat-file blob $GIT_PUSH_CERT >../push-cert
276 fi &&
277
278 cat >../push-cert-status <<E_O_F
279 SIGNER=${GIT_PUSH_CERT_SIGNER-nobody}
280 KEY=${GIT_PUSH_CERT_KEY-nokey}
281 STATUS=${GIT_PUSH_CERT_STATUS-nostatus}
282 NONCE_STATUS=${GIT_PUSH_CERT_NONCE_STATUS-nononcestatus}
283 NONCE=${GIT_PUSH_CERT_NONCE-nononce}
284 E_O_F
285
286 EOF
287
288 test_config user.email hasnokey@nowhere.com &&
289 test_config user.signingkey "" &&
290 (
291 sane_unset GIT_COMMITTER_EMAIL &&
292 test_must_fail git push --signed dst noop ff +noff
293 ) &&
294 test_config user.signingkey $GIT_COMMITTER_EMAIL &&
295 git push --signed dst noop ff +noff &&
296
297 (
298 cat <<-\EOF &&
299 SIGNER=/CN=C O Mitter/O=Example/SN=C O/GN=Mitter
300 KEY=
301 STATUS=G
302 NONCE_STATUS=OK
303 EOF
304 sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert
305 ) >expect.in &&
306 key=$(cut -d" " -f1 <"${GNUPGHOME}/trustlist.txt" | tr -d ":") &&
307 sed -e "s/^KEY=/KEY=${key}/" expect.in >expect &&
308
309 noop=$(git rev-parse noop) &&
310 ff=$(git rev-parse ff) &&
311 noff=$(git rev-parse noff) &&
312 grep "$noop $ff refs/heads/ff" dst/push-cert &&
313 grep "$noop $noff refs/heads/noff" dst/push-cert &&
314 test_cmp expect dst/push-cert-status
315 '
316
317 test_expect_success GPGSSH 'fail without key and heed user.signingkey ssh' '
318 test_config gpg.format ssh &&
319 prepare_dst &&
320 git -C dst config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
321 git -C dst config receive.certnonceseed sekrit &&
322 test_hook -C dst post-receive <<-\EOF &&
323 # discard the update list
324 cat >/dev/null
325 # record the push certificate
326 if test -n "${GIT_PUSH_CERT-}"
327 then
328 git cat-file blob $GIT_PUSH_CERT >../push-cert
329 fi &&
330
331 cat >../push-cert-status <<E_O_F
332 SIGNER=${GIT_PUSH_CERT_SIGNER-nobody}
333 KEY=${GIT_PUSH_CERT_KEY-nokey}
334 STATUS=${GIT_PUSH_CERT_STATUS-nostatus}
335 NONCE_STATUS=${GIT_PUSH_CERT_NONCE_STATUS-nononcestatus}
336 NONCE=${GIT_PUSH_CERT_NONCE-nononce}
337 E_O_F
338
339 EOF
340
341 test_config user.email hasnokey@nowhere.com &&
342 test_config gpg.format ssh &&
343 test_config user.signingkey "" &&
344 (
345 sane_unset GIT_COMMITTER_EMAIL &&
346 test_must_fail git push --signed dst noop ff +noff
347 ) &&
348 test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" &&
349 FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_PRIMARY}" | awk "{print \$2;}") &&
350 git push --signed dst noop ff +noff &&
351
352 (
353 cat <<-\EOF &&
354 SIGNER=principal with number 1
355 KEY=FINGERPRINT
356 STATUS=G
357 NONCE_STATUS=OK
358 EOF
359 sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert
360 ) | sed -e "s|FINGERPRINT|$FINGERPRINT|" >expect &&
361
362 noop=$(git rev-parse noop) &&
363 ff=$(git rev-parse ff) &&
364 noff=$(git rev-parse noff) &&
365 grep "$noop $ff refs/heads/ff" dst/push-cert &&
366 grep "$noop $noff refs/heads/noff" dst/push-cert &&
367 test_cmp expect dst/push-cert-status
368 '
369
370 test_expect_success GPG 'failed atomic push does not execute GPG' '
371 prepare_dst &&
372 git -C dst config receive.certnonceseed sekrit &&
373 write_script gpg <<-EOF &&
374 # should check atomic push locally before running GPG.
375 exit 1
376 EOF
377 test_must_fail env PATH="$TRASH_DIRECTORY:$PATH" git push \
378 --signed --atomic --porcelain \
379 dst noop ff noff >out 2>err &&
380
381 test_grep ! "gpg failed to sign" err &&
382 cat >expect <<-EOF &&
383 To dst
384 = refs/heads/noop:refs/heads/noop [up to date]
385 ! refs/heads/ff:refs/heads/ff [rejected] (atomic push failed)
386 ! refs/heads/noff:refs/heads/noff [rejected] (non-fast-forward)
387 Done
388 EOF
389 test_cmp expect out
390 '
391
392 test_done