Merge branch 'js/fsmonitor-unpack-fix'
[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         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
46         fi
47         EOF
48
49         git push dst noop ff +noff &&
50         ! test -f dst/push-cert
51 '
52
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
63         fi
64         EOF
65
66         git push dst noop ff +noff &&
67         ! test -f dst/push-cert
68 '
69
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
75 '
76
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
82 '
83
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
91         fi
92         EOF
93         git push dst noop &&
94         ! test -f dst/push-cert
95 '
96
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 &&
109
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
117
118         EOF
119
120         git push --signed dst noop ff +noff &&
121
122         (
123                 cat <<-\EOF &&
124                 SIGNER=C O Mitter <committer@example.com>
125                 KEY=13B6F51ECDDE430D
126                 STATUS=G
127                 NONCE_STATUS=OK
128                 EOF
129                 sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert
130         ) >expect &&
131
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
138 '
139
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.
142         prepare_dst &&
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 &&
146
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 &&
150                 cat preamble &&
151                 printf xxxx &&
152                 cat >push
153         EOF
154         test_might_fail git push --push-option="foo" --push-option="bar" \
155                 --receive-pack="\"$(pwd)/myscript\"" --signed dst --delete ff &&
156
157         # Replay the push output on a fresh dst, checking that ff is truly
158         # deleted.
159         prepare_dst &&
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 &&
164
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
167         # deleted.
168         perl -pe "s/([^ ])bar/\$1baz/" push >push.tweak &&
169         prepare_dst &&
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
175 '
176
177 test_expect_success GPG 'fail without key and heed user.signingkey' '
178         prepare_dst &&
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
183         cat >/dev/null
184         # record the push certificate
185         if test -n "${GIT_PUSH_CERT-}"
186         then
187                 git cat-file blob $GIT_PUSH_CERT >../push-cert
188         fi &&
189
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}
196         E_O_F
197
198         EOF
199
200         test_config user.email hasnokey@nowhere.com &&
201         (
202                 sane_unset GIT_COMMITTER_EMAIL &&
203                 test_must_fail git push --signed dst noop ff +noff
204         ) &&
205         test_config user.signingkey $GIT_COMMITTER_EMAIL &&
206         git push --signed dst noop ff +noff &&
207
208         (
209                 cat <<-\EOF &&
210                 SIGNER=C O Mitter <committer@example.com>
211                 KEY=13B6F51ECDDE430D
212                 STATUS=G
213                 NONCE_STATUS=OK
214                 EOF
215                 sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert
216         ) >expect &&
217
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
224 '
225
226 test_expect_success GPGSM 'fail without key and heed user.signingkey x509' '
227         test_config gpg.format x509 &&
228         prepare_dst &&
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
233         cat >/dev/null
234         # record the push certificate
235         if test -n "${GIT_PUSH_CERT-}"
236         then
237                 git cat-file blob $GIT_PUSH_CERT >../push-cert
238         fi &&
239
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}
246         E_O_F
247
248         EOF
249
250         test_config user.email hasnokey@nowhere.com &&
251         test_config user.signingkey "" &&
252         (
253                 sane_unset GIT_COMMITTER_EMAIL &&
254                 test_must_fail git push --signed dst noop ff +noff
255         ) &&
256         test_config user.signingkey $GIT_COMMITTER_EMAIL &&
257         git push --signed dst noop ff +noff &&
258
259         (
260                 cat <<-\EOF &&
261                 SIGNER=/CN=C O Mitter/O=Example/SN=C O/GN=Mitter
262                 KEY=
263                 STATUS=G
264                 NONCE_STATUS=OK
265                 EOF
266                 sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert
267         ) >expect.in &&
268         key=$(cat "${GNUPGHOME}/trustlist.txt" | cut -d" " -f1 | tr -d ":") &&
269         sed -e "s/^KEY=/KEY=${key}/" expect.in >expect &&
270
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
277 '
278
279 test_expect_success GPG 'failed atomic push does not execute GPG' '
280         prepare_dst &&
281         git -C dst config receive.certnonceseed sekrit &&
282         write_script gpg <<-EOF &&
283         # should check atomic push locally before running GPG.
284         exit 1
285         EOF
286         test_must_fail env PATH="$TRASH_DIRECTORY:$PATH" git push \
287                         --signed --atomic --porcelain \
288                         dst noop ff noff >out 2>err &&
289
290         test_i18ngrep ! "gpg failed to sign" err &&
291         cat >expect <<-EOF &&
292         To dst
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)
296         Done
297         EOF
298         test_cmp expect out
299 '
300
301 test_done