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