3 test_description
='signed push'
5 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
=main
6 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
9 .
"$TEST_DIRECTORY"/lib-gpg.sh
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
21 git commit --allow-empty -m initial &&
23 git checkout -b noop &&
25 git checkout -b noff &&
27 # noop stays the same, ff advances, noff rewrites
29 git commit --allow-empty --amend -m rewritten &&
33 git commit --allow-empty -m second
36 test_expect_success
'unsigned push does not send push certificate' '
38 mkdir -p dst/.git/hooks &&
39 write_script dst/.git/hooks/post-receive <<-\EOF &&
40 # discard the update list
42 # record the push certificate
43 if test -n "${GIT_PUSH_CERT-}"
45 git cat-file blob $GIT_PUSH_CERT >../push-cert
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' '
55 mkdir -p dst/.git/hooks &&
56 write_script dst/.git/hooks/post-receive <<-\EOF &&
57 # discard the update list
59 # record the push certificate
60 if test -n "${GIT_PUSH_CERT-}"
62 git cat-file blob $GIT_PUSH_CERT >../push-cert
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' '
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' '
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' '
86 mkdir -p dst/.git/hooks &&
87 write_script dst/.git/hooks/post-receive <<-\EOF &&
88 if test -n "${GIT_PUSH_CERT-}"
90 git cat-file blob $GIT_PUSH_CERT >../push-cert
94 ! test -f dst/push-cert
97 test_expect_success GPG
'signed push sends push certificate' '
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
104 # record the push certificate
105 if test -n "${GIT_PUSH_CERT-}"
107 git cat-file blob $GIT_PUSH_CERT >../push-cert
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}
120 git push --signed dst noop ff +noff &&
124 SIGNER=C O Mitter <committer@example.com>
129 sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert
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' '
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
148 # record the push certificate
149 if test -n "${GIT_PUSH_CERT-}"
151 git cat-file blob $GIT_PUSH_CERT >../push-cert
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}
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 &&
171 SIGNER=principal with number 1
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.
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 &&
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
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
215 perl -pe "s/([^ ])bar/\$1baz/" push >push.tweak &&
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' '
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
231 # record the push certificate
232 if test -n "${GIT_PUSH_CERT-}"
234 git cat-file blob $GIT_PUSH_CERT >../push-cert
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}
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
252 test_config user.signingkey $GIT_COMMITTER_EMAIL &&
253 git push --signed dst noop ff +noff &&
257 SIGNER=C O Mitter <committer@example.com>
262 sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert
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 &&
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
281 # record the push certificate
282 if test -n "${GIT_PUSH_CERT-}"
284 git cat-file blob $GIT_PUSH_CERT >../push-cert
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}
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
303 test_config user.signingkey $GIT_COMMITTER_EMAIL &&
304 git push --signed dst noop ff +noff &&
308 SIGNER=/CN=C O Mitter/O=Example/SN=C O/GN=Mitter
313 sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert
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 &&
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
335 # record the push certificate
336 if test -n "${GIT_PUSH_CERT-}"
338 git cat-file blob $GIT_PUSH_CERT >../push-cert
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}
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
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 &&
364 SIGNER=principal with number 1
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' '
382 git -C dst config receive.certnonceseed sekrit &&
383 write_script gpg <<-EOF &&
384 # should check atomic push locally before running GPG.
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 &&
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)