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 GPG 'inconsistent push options in signed push not allowed' '
141 # First, invoke receive-pack with dummy input to obtain its preamble.
143 git -C dst config receive.certnonceseed sekrit &&
144 git -C dst config receive.advertisepushoptions 1 &&
145 printf xxxx | test_might_fail git receive-pack dst >preamble &&
147 # Then, invoke push. Simulate a receive-pack that sends the preamble we
148 # obtained, followed by a dummy packet.
149 write_script myscript <<-\EOF &&
154 test_might_fail git push --push-option="foo" --push-option="bar" \
155 --receive-pack="\"$(pwd)/myscript\"" --signed dst --delete ff &&
157 # Replay the push output on a fresh dst, checking that ff is truly
160 git -C dst config receive.certnonceseed sekrit &&
161 git -C dst config receive.advertisepushoptions 1 &&
162 git receive-pack dst <push &&
163 test_must_fail git -C dst rev-parse ff &&
165 # Tweak the push output to make the push option outside the cert
166 # different, then replay it on a fresh dst, checking that ff is not
168 perl -pe "s/([^ ])bar/\$1baz/" push >push.tweak &&
170 git -C dst config receive.certnonceseed sekrit &&
171 git -C dst config receive.advertisepushoptions 1 &&
172 git receive-pack dst <push.tweak >out &&
173 git -C dst rev-parse ff &&
174 grep "inconsistent push options" out
177 test_expect_success GPG 'fail without key and heed user.signingkey' '
179 mkdir -p dst/.git/hooks &&
180 git -C dst config receive.certnonceseed sekrit &&
181 write_script dst/.git/hooks/post-receive <<-\EOF &&
182 # discard the update list
184 # record the push certificate
185 if test -n "${GIT_PUSH_CERT-}"
187 git cat-file blob $GIT_PUSH_CERT >../push-cert
190 cat >../push-cert-status <<E_O_F
191 SIGNER=${GIT_PUSH_CERT_SIGNER-nobody}
192 KEY=${GIT_PUSH_CERT_KEY-nokey}
193 STATUS=${GIT_PUSH_CERT_STATUS-nostatus}
194 NONCE_STATUS=${GIT_PUSH_CERT_NONCE_STATUS-nononcestatus}
195 NONCE=${GIT_PUSH_CERT_NONCE-nononce}
200 test_config user.email hasnokey@nowhere.com &&
202 sane_unset GIT_COMMITTER_EMAIL &&
203 test_must_fail git push --signed dst noop ff +noff
205 test_config user.signingkey $GIT_COMMITTER_EMAIL &&
206 git push --signed dst noop ff +noff &&
210 SIGNER=C O Mitter <committer@example.com>
215 sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert
218 noop=$(git rev-parse noop) &&
219 ff=$(git rev-parse ff) &&
220 noff=$(git rev-parse noff) &&
221 grep "$noop $ff refs/heads/ff" dst/push-cert &&
222 grep "$noop $noff refs/heads/noff" dst/push-cert &&
223 test_cmp expect dst/push-cert-status
226 test_expect_success GPGSM 'fail without key and heed user.signingkey x509' '
227 test_config gpg.format x509 &&
229 mkdir -p dst/.git/hooks &&
230 git -C dst config receive.certnonceseed sekrit &&
231 write_script dst/.git/hooks/post-receive <<-\EOF &&
232 # discard the update list
234 # record the push certificate
235 if test -n "${GIT_PUSH_CERT-}"
237 git cat-file blob $GIT_PUSH_CERT >../push-cert
240 cat >../push-cert-status <<E_O_F
241 SIGNER=${GIT_PUSH_CERT_SIGNER-nobody}
242 KEY=${GIT_PUSH_CERT_KEY-nokey}
243 STATUS=${GIT_PUSH_CERT_STATUS-nostatus}
244 NONCE_STATUS=${GIT_PUSH_CERT_NONCE_STATUS-nononcestatus}
245 NONCE=${GIT_PUSH_CERT_NONCE-nononce}
250 test_config user.email hasnokey@nowhere.com &&
251 test_config user.signingkey "" &&
253 sane_unset GIT_COMMITTER_EMAIL &&
254 test_must_fail git push --signed dst noop ff +noff
256 test_config user.signingkey $GIT_COMMITTER_EMAIL &&
257 git push --signed dst noop ff +noff &&
261 SIGNER=/CN=C O Mitter/O=Example/SN=C O/GN=Mitter
266 sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert
268 key=$(cat "${GNUPGHOME}/trustlist.txt" | cut -d" " -f1 | tr -d ":") &&
269 sed -e "s/^KEY=/KEY=${key}/" expect.in >expect &&
271 noop=$(git rev-parse noop) &&
272 ff=$(git rev-parse ff) &&
273 noff=$(git rev-parse noff) &&
274 grep "$noop $ff refs/heads/ff" dst/push-cert &&
275 grep "$noop $noff refs/heads/noff" dst/push-cert &&
276 test_cmp expect dst/push-cert-status
279 test_expect_success GPG 'failed atomic push does not execute GPG' '
281 git -C dst config receive.certnonceseed sekrit &&
282 write_script gpg <<-EOF &&
283 # should check atomic push locally before running GPG.
286 test_must_fail env PATH="$TRASH_DIRECTORY:$PATH" git push \
287 --signed --atomic --porcelain \
288 dst noop ff noff >out 2>err &&
290 test_i18ngrep ! "gpg failed to sign" err &&
291 cat >expect <<-EOF &&
293 = refs/heads/noop:refs/heads/noop [up to date]
294 ! refs/heads/ff:refs/heads/ff [rejected] (atomic push failed)
295 ! refs/heads/noff:refs/heads/noff [rejected] (non-fast-forward)