From: "Sebastian Götte" <jaseg@physik•tu-berlin.de>
To: git@vger•kernel.org
Cc: gitster@pobox•com
Subject: [PATCH] merge/pull: verify GPG signatures of commits being merged
Date: Fri, 22 Mar 2013 22:51:40 +0100 [thread overview]
Message-ID: <514CD26C.2070702@physik.tu-berlin.de> (raw)
git merge/pull:
When --verify-signatures is specified on the command-line of git-merge
or git-pull, check whether the commits being merged have good gpg
signatures and abort the merge in case they do not. This allows e.g.
auto-deployment from untrusted repo hosts.
pretty printing:
Tell about an "untrusted good signature" in addition to the previous
"good signature" and "bad signature". In case of a missing signature,
expand the pretty format string "%G?" to "N" in since this eases the
wirting of anything parsing git-log output.
Signed-off-by: Sebastian Götte <jaseg@physik-pool•tu-berlin.de>
---
I moved the commit signature verification code from pretty.c to commit.c
because it is used from pretty.c and builtin/merge.c. I include that pretty
printing change here because I needed to add the good/untrusted check for the
merge --verify-signatures option to really make sense.
Those new %G? options are implicitly tested by t7612-merge-verify-signatures.sh
because %G? is just replaced by the passed-through output of the commit
verification function.
Documentation/merge-options.txt | 4 +++
Documentation/pretty-formats.txt | 2 +-
builtin/merge.c | 33 +++++++++++++++++-
commit.c | 55 +++++++++++++++++++++++++++++
commit.h | 9 +++++
git-pull.sh | 10 ++++--
gpg-interface.h | 6 ++++
pretty.c | 69 ++++---------------------------------
t/lib-gpg/pubring.gpg | Bin 1164 -> 2359 bytes
t/lib-gpg/random_seed | Bin 600 -> 600 bytes
t/lib-gpg/secring.gpg | Bin 1237 -> 3734 bytes
t/lib-gpg/trustdb.gpg | Bin 1280 -> 1360 bytes
t/t7612-merge-verify-signatures.sh | 61 ++++++++++++++++++++++++++++++++
13 files changed, 183 insertions(+), 66 deletions(-)
diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt
index 0bcbe0a..2f76ab5 100644
--- a/Documentation/merge-options.txt
+++ b/Documentation/merge-options.txt
@@ -83,6 +83,10 @@ option can be used to override --squash.
Pass merge strategy specific option through to the merge
strategy.
+--verify-signatures::
+--no-verify-signatures::
+ Verify that the commits being merged have good trusted GPG signatures
+
--summary::
--no-summary::
Synonyms to --stat and --no-stat; these are deprecated and will be
diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt
index 105f18a..7297b1b 100644
--- a/Documentation/pretty-formats.txt
+++ b/Documentation/pretty-formats.txt
@@ -131,7 +131,7 @@ The placeholders are:
- '%B': raw body (unwrapped subject and body)
- '%N': commit notes
- '%GG': raw verification message from GPG for a signed commit
-- '%G?': show either "G" for Good or "B" for Bad for a signed commit
+- '%G?': show "G" for a Good signature, "B" for a Bad signature, "U" for a good, untrusted signature and "N" for no signature
- '%GS': show the name of the signer for a signed commit
- '%gD': reflog selector, e.g., `refs/stash@{1}`
- '%gd': shortened reflog selector, e.g., `stash@{1}`
diff --git a/builtin/merge.c b/builtin/merge.c
index 7c8922c..37ece3d 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -49,7 +49,7 @@ static const char * const builtin_merge_usage[] = {
static int show_diffstat = 1, shortlog_len = -1, squash;
static int option_commit = 1, allow_fast_forward = 1;
static int fast_forward_only, option_edit = -1;
-static int allow_trivial = 1, have_message;
+static int allow_trivial = 1, have_message, verify_signatures = 0;
static int overwrite_ignore = 1;
static struct strbuf merge_msg = STRBUF_INIT;
static struct strategy **use_strategies;
@@ -199,6 +199,8 @@ static struct option builtin_merge_options[] = {
OPT_BOOLEAN(0, "ff-only", &fast_forward_only,
N_("abort if fast-forward is not possible")),
OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
+ OPT_BOOLEAN(0, "verify-signatures", &verify_signatures,
+ N_("Verify that the named commit has a valid GPG signature")),
OPT_CALLBACK('s', "strategy", &use_strategies, N_("strategy"),
N_("merge strategy to use"), option_parse_strategy),
OPT_CALLBACK('X', "strategy-option", &xopts, N_("option=value"),
@@ -1233,6 +1235,35 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
usage_with_options(builtin_merge_usage,
builtin_merge_options);
+ if (verify_signatures) {
+ //Verify the commit signatures
+ for (p = remoteheads; p; p = p->next) {
+ struct commit *commit = p->item;
+ struct signature signature;
+ signature.check_result = 0;
+
+ check_commit_signature(commit, &signature);
+
+ char hex[41];
+ strcpy(hex, find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));
+ switch(signature.check_result){
+ case 'G':
+ if (verbosity >= 0)
+ printf(_("Commit %s has a good GPG signature by %s\n"), hex, signature.signer);
+ break;
+ case 'U':
+ die(_("Commmit %s has a good GPG signature allegedly by %s, albeit from an untrusted key."), hex, signature.signer);
+ case 'B':
+ die(_("Commmit %s has a bad GPG signature allegedly by %s."), hex, signature.signer);
+ default: //'N'
+ die(_("Commmit %s does not have a good GPG signature. In fact, commit %s does not have a GPG signature at all."), hex, hex);
+ }
+
+ free(signature.gpg_output);
+ free(signature.signer);
+ }
+ }
+
strbuf_addstr(&buf, "merge");
for (p = remoteheads; p; p = p->next)
strbuf_addf(&buf, " %s", merge_remote_util(p->item)->name);
diff --git a/commit.c b/commit.c
index e8eb0ae..300cf98 100644
--- a/commit.c
+++ b/commit.c
@@ -1023,6 +1023,61 @@ free_return:
free(buf);
}
+static struct {
+ char result;
+ const char *check;
+} signature_check[] = {
+ { 'U', ": WARNING: This key is not certified with a trusted signature!" },
+ { 'G', ": Good signature from " },
+ { 'B', ": BAD signature from " },
+};
+
+static void parse_signature_lines(struct signature *sig)
+{
+ const char *buf = sig->gpg_output;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(signature_check); i++) {
+ const char *found = strstr(buf, signature_check[i].check);
+ const char *next;
+ if (!found)
+ continue;
+ sig->check_result = signature_check[i].result;
+ found += strlen(signature_check[i].check);
+ next = strchrnul(found, '\n');
+ sig->signer = xmemdupz(found, next - found);
+ break;
+ }
+}
+
+void check_commit_signature(const struct commit* commit, struct signature *sig)
+{
+ struct strbuf payload = STRBUF_INIT;
+ struct strbuf signature = STRBUF_INIT;
+ struct strbuf gpg_output = STRBUF_INIT;
+ int status;
+
+ sig->check_result = 'N';
+
+ if (parse_signed_commit(commit->object.sha1,
+ &payload, &signature) <= 0)
+ goto out;
+ status = verify_signed_buffer(payload.buf, payload.len,
+ signature.buf, signature.len,
+ &gpg_output);
+ if (status && !gpg_output.len)
+ goto out;
+ sig->gpg_output = strbuf_detach(&gpg_output, NULL);
+ parse_signature_lines(sig);
+
+ out:
+ strbuf_release(&gpg_output);
+ strbuf_release(&payload);
+ strbuf_release(&signature);
+}
+
+
+
void append_merge_tag_headers(struct commit_list *parents,
struct commit_extra_header ***tail)
{
diff --git a/commit.h b/commit.h
index 4138bb4..2189eb6 100644
--- a/commit.h
+++ b/commit.h
@@ -5,6 +5,7 @@
#include "tree.h"
#include "strbuf.h"
#include "decorate.h"
+#include "gpg-interface.h"
struct commit_list {
struct commit *item;
@@ -230,4 +231,12 @@ extern void print_commit_list(struct commit_list *list,
const char *format_cur,
const char *format_last);
+/*
+ * Check the signature of the given commit. The result of the check is stored in
+ * sig->check_result, 'G' for a good signature, 'U' for a good signature from an
+ * untrusted signer, 'B' for a bad signature and 'N' for no signature at all.
+ * This may allocate memory for sig->gpg_output and sig->signer.
+ */
+extern void check_commit_signature(const struct commit* commit, struct signature *sig);
+
#endif /* COMMIT_H */
diff --git a/git-pull.sh b/git-pull.sh
index 266e682..705940d 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -39,7 +39,7 @@ test -z "$(git ls-files -u)" || die_conflict
test -f "$GIT_DIR/MERGE_HEAD" && die_merge
strategy_args= diffstat= no_commit= squash= no_ff= ff_only=
-log_arg= verbosity= progress= recurse_submodules=
+log_arg= verbosity= progress= recurse_submodules= verify_signatures=
merge_args= edit=
curr_branch=$(git symbolic-ref -q HEAD)
curr_branch_short="${curr_branch#refs/heads/}"
@@ -125,6 +125,12 @@ do
--no-recurse-submodules)
recurse_submodules=--no-recurse-submodules
;;
+ --verify-signatures)
+ verify_signatures=--verify-signatures
+ ;;
+ --no-verify-signatures)
+ verify_signatures=--no-verify-signatures
+ ;;
--d|--dr|--dry|--dry-|--dry-r|--dry-ru|--dry-run)
dry_run=--dry-run
;;
@@ -283,7 +289,7 @@ true)
eval="$eval --onto $merge_head ${oldremoteref:-$merge_head}"
;;
*)
- eval="git-merge $diffstat $no_commit $edit $squash $no_ff $ff_only"
+ eval="git-merge $diffstat $no_commit $verify_signatures $edit $squash $no_ff $ff_only"
eval="$eval $log_arg $strategy_args $merge_args $verbosity $progress"
eval="$eval \"\$merge_name\" HEAD $merge_head"
;;
diff --git a/gpg-interface.h b/gpg-interface.h
index b9c3608..3895b2f 100644
--- a/gpg-interface.h
+++ b/gpg-interface.h
@@ -1,6 +1,12 @@
#ifndef GPG_INTERFACE_H
#define GPG_INTERFACE_H
+struct signature {
+ char *gpg_output;
+ char check_result; // 0 (not checked), N (checked but no further result), U (untrusted, good), G (good) or B (bad)
+ char *signer;
+};
+
extern int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *signing_key);
extern int verify_signed_buffer(const char *payload, size_t payload_size, const char *signature, size_t signature_size, struct strbuf *gpg_output);
extern int git_gpg_config(const char *, const char *, void *);
diff --git a/pretty.c b/pretty.c
index eae57ad..875a1db 100644
--- a/pretty.c
+++ b/pretty.c
@@ -756,12 +756,7 @@ struct format_commit_context {
const struct pretty_print_context *pretty_ctx;
unsigned commit_header_parsed:1;
unsigned commit_message_parsed:1;
- unsigned commit_signature_parsed:1;
- struct {
- char *gpg_output;
- char good_bad;
- char *signer;
- } signature;
+ struct signature signature;
char *message;
size_t width, indent1, indent2;
@@ -944,58 +939,6 @@ static void rewrap_message_tail(struct strbuf *sb,
c->indent2 = new_indent2;
}
-static struct {
- char result;
- const char *check;
-} signature_check[] = {
- { 'G', ": Good signature from " },
- { 'B', ": BAD signature from " },
-};
-
-static void parse_signature_lines(struct format_commit_context *ctx)
-{
- const char *buf = ctx->signature.gpg_output;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(signature_check); i++) {
- const char *found = strstr(buf, signature_check[i].check);
- const char *next;
- if (!found)
- continue;
- ctx->signature.good_bad = signature_check[i].result;
- found += strlen(signature_check[i].check);
- next = strchrnul(found, '\n');
- ctx->signature.signer = xmemdupz(found, next - found);
- break;
- }
-}
-
-static void parse_commit_signature(struct format_commit_context *ctx)
-{
- struct strbuf payload = STRBUF_INIT;
- struct strbuf signature = STRBUF_INIT;
- struct strbuf gpg_output = STRBUF_INIT;
- int status;
-
- ctx->commit_signature_parsed = 1;
-
- if (parse_signed_commit(ctx->commit->object.sha1,
- &payload, &signature) <= 0)
- goto out;
- status = verify_signed_buffer(payload.buf, payload.len,
- signature.buf, signature.len,
- &gpg_output);
- if (status && !gpg_output.len)
- goto out;
- ctx->signature.gpg_output = strbuf_detach(&gpg_output, NULL);
- parse_signature_lines(ctx);
-
- out:
- strbuf_release(&gpg_output);
- strbuf_release(&payload);
- strbuf_release(&signature);
-}
-
static int format_reflog_person(struct strbuf *sb,
char part,
@@ -1182,18 +1125,20 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,
}
if (placeholder[0] == 'G') {
- if (!c->commit_signature_parsed)
- parse_commit_signature(c);
+ if (!c->signature.check_result)
+ check_commit_signature(c->commit, &(c->signature));
switch (placeholder[1]) {
case 'G':
if (c->signature.gpg_output)
strbuf_addstr(sb, c->signature.gpg_output);
break;
case '?':
- switch (c->signature.good_bad) {
+ switch (c->signature.check_result) {
case 'G':
case 'B':
- strbuf_addch(sb, c->signature.good_bad);
+ case 'U':
+ case 'N':
+ strbuf_addch(sb, c->signature.check_result);
}
break;
case 'S':
diff --git a/t/lib-gpg/pubring.gpg b/t/lib-gpg/pubring.gpg
index 83855fa4e1c6c37afe550c17afa1e7971042ded5..1a3c2d487c2fda9169751a3068fa51e853a1e519 100644
GIT binary patch
delta 1212
zcmV;t1Vj6b3AYlkj0As~0SyFEOqNFh2mr~8{AU1PG9!Ku9w|}@vpZJPg*#s86v-*O
zhafj(DL&&lF<A0)`tvC$E@WHJ2r~0{0ZCh1kHo$b9ih^aD*~)oVvK&yC1(yi)6x_y
zF8V3JpbIY^ZYQ&<Uk#j*ja0`;jw^J+5~!h3qc)ej%g1;Wb0U8cXSuLKdXkx2PUtB4
z>zqQ>O;r+6Qa<VFd?Z<kA#8Ch(&2p+QaoKR;K0{BU;l_DqGU2YCt6Fr4psgB*tPou
z8C@tnj=CqkffSLs=a-5G+h;Y8bkw<s-iIgT{$f^j!)l`hmkz}$nZPxBoPmwU;zIH^
zLN~K=bO>hGBes9{Fcqzh%Wj2h^{ZjI01*KI0kkAVa%poQL}_zlZ*pX5VIVwYX>((5
za%4bdcwudDY-KKPWpqA?0XPH`0RjLb1p-k_mPY~`0|pBT2nPcK1{DYb2?`4Y76JnS
z0v-VZ7k~f?2@qikE`_%uafw)?2m1%-{uDP0Rs1|(F{OVhOSKoH$k!o)x3CO5Z^@El
zxGq=+xQGR&r@3$S<0@SUu@@UuxUH%pD3Q-D`?37Vg*xmo<%+KD)(U~k>XU^b+=70V
zFr?b3DvlFq^B*d%lz=lr!ch6nQbvcwWCD}n2DT3`r?RDkq=7r}qFvEh<dfApA7yR;
z8`OB;$TEKm@jkwG=8vlcKFd>rcM8+9eP66QueaUSz6NhphD3O@E(iX7T@2kxV(dXd
zxG_$z;qqdh<Np6*{J!OD7<b;8M6otxRi0UUN|OngZ4NdYbeU0hneiVEwY2h5xZ4z{
zsXL4>UkGxJ?Pc}MK(e<Q>lV@oumS)Bxd9CXQA~f9M*#=`sXI><#Mh|Ll%uxJqK}<$
zc)VOU|M7H$nU~Bd&(!9WlLk)V)&z>UY~vSc_rVFe1JAY7ThlG1IzE9z6>ly;9W<Ie
zrW&+EU|2KpxZ$0rq-Jn9T{vsi6Qh0j42I3_PLQ5$)qa$}3etj89nJkr^3#KD!xh9*
z4BvmZ0TM||zk);(8@JN=fad9d4hK=Hf1{&&J5DP=<J9wW0IGZ_*p$xvH=WZu8h1r#
z-&1Cjfqp9S1m|b%7<S$7Du_^GfwyFyObC4EMw~6<SwH1<W45gXD*OLAz9x~@tJ8<?
z^Jtn;2$K^0SUY?f@Cu@)V5#TJ_yB~Qw!nY4S8m<?1+xGV0RRDs0Urby0RjLC1p-k_
zmPY~`3;+rV5Mc=}g|{MciR^I*0LA1n{v0`JrvmHmaVfVdlHi8rqLbHhetm3X@&@Ir
zXeh6;re%LlP-QWig+n`!Ae0mFQrnvwOO!ZC8%_&sj-2tVNM{p{WTNusbkj>Mdd`2^
zgn2IBiSf%lm^DyQau7lg)pfK8(7<}-iih0<Mm(k9C_6rNl4sE(`#Aa}K2LGjw*;6_
zqt%Wq<wPe#N15ysVyQhrRmcBJ^&Q!GEW&Mp#@%c+q~S#5UAR4_eeRejBs87Xv&Yt)
zg^u)$-LG%(tX4KA^%L!j<@*Z_A!anKyx}t&F6RX0yYXc)&KtB~l*=nGq9`cf<aD`r
a9*zs=xM)EjeiKg!$s4>o369jT0ssS^s3f2O
delta 7
Ocmdlk)Wf-<hXnu&fC8TY
diff --git a/t/lib-gpg/random_seed b/t/lib-gpg/random_seed
index 8fed1339ed0a744e5663f4a5e6b6ac9bae3d8524..95d249f15fce980f0e8c1a8a18b085b3885708aa 100644
GIT binary patch
literal 600
zcmV-e0;m1ccZd+x>>TST*Lrq1x^ggx^+ymwieO!6X=U~ZH@{avIgxdn#ai{)Ou@Qw
za}Z!boffEq^fn)n?c=IEnDpt59Lnc)aR*;8Z;k>gh_NW;ka;7Mt@v#sG(!Y9SSXWv
zQxd3WlyBr#4ltW6uKOoa6(r3df1VX$cG4`Om6hD-ckaX+Hb_yI?{f`hJQY&k!1cM-
zoGeY~(Z7aYn$W06djh?W|CMs>W=k@jgf=P2D1UA1T%vz0oE|<O<lIacG0xioPtS&U
zNd#}P%YpJr-H65~J^RdqA!YV9BEvh7Gw^CdXg+Hp?kj=KGW|+|&g$4?`trWWGuy$9
zv-|;8Y4(NRHWPyJ{epd{4%FHQKk5j}?0FFDAJ;0kIItZ4y<JS?DIG4~0!#x~;X`!P
zO%+va?@`?yQnhjrP@&#yjY$YO_0yk|1ddhc8V&ru7d%ytet)mF<ZIUbPB3bvhHQ41
zNmnYeFxUMu=m$K5&s=5_F&JSR#oU3Y#X{(q7HTp-VYJ)%JjihbZ@R#GeqmU{>0C4Q
zc}hUG+ighB{7XSaNw_h;=YtqacQ<B(Cg$e)^NTDD-oMD+T`O#-^|-ib>j!<pxHg+(
zlC$%zE836|E*F*((=>O{Nn@K$taZO}!>$t>GMgsw?!=n_#(%X9Ha|$b=H@VstWYe;
zPUQ<L$$#9HTcOLoyEd6*A4TOEe3}c}GiW*^P1Lt{nHYUEAB`Qx7*wizaEyM$?AjVN
mb-6m)4=6PVqdR>h+D!{<c#q1!T9b(}OW7hrrT@nJcBO(OGA4ll
literal 600
zcmV-e0;m1=h9nBV>1C6QsKJEiEJaD@Q3F8s5u<$E+<2(By)JAZSxviTsXg(wKC+O%
zzvV{Z>W3*k?r7~pgmmkbw8-x{Am!eeN)z?cwIHcT2jqgiA(SXo<iO=E?cY80`p#w8
z)O-&?SnwsJ=1VJ-?26&*g88Nr8E=g2onRW^(c+2nJlX)?dmK)tPO0EY-!B!vMCv1)
z-AOW(3WuF+7IdSxMnzrDgnMqVU=|+YFxlY|VeR+Fg<%C@0Xupi0<S7QYJyFTR$}FQ
zzoSAbU>CoCKWKX;!3@L_U=aFUm!M<>ILG}$`bfnadAkLQbI-upV7Qwf^OE&N45Pz<
zk~^KlzNC6)d@QGv=K5-At&A8FS&MQSR`LB}@R1?A3K1p(vM>7CK}EfFhmBJd&cH^-
z(3Ih^`VuoVBB|w~p!Q^#DY%V2A2FhXu<Bp*L)lSCUdqRyI5wxMG&E1sL$)E$Zo&pJ
zgy#;fENqHImgN>LL2!7DhfZ}&;BSAyz=T0#S?2+NET5St@16L?YI?5Io%<uD|2}hl
zx0xsuefz1+bM^-ZIgtKs=)&VAI8(MfytvM>t>%~nsXUb~*EkptHiN?W{=DRu_s;2u
ziHh{2&>;CQO7;>{$DN33_Ef}g+;b<2hIF^p(Y>^riLBb*Y2Xw>F8)jp49&oLKJOic
z+V{Lt!_`eKGhyk5Edie{-^#n!TFlsfux*QBRZEh^4SVePPmb{BvF|>sKd2cYg@vKp
mVI8jcB1(k(tlt^Kr<{EMs>|b*d70nyVMQcc%xEnE(#Uq3d^-35
diff --git a/t/lib-gpg/secring.gpg b/t/lib-gpg/secring.gpg
index d831cd9eb3eee613d3c0e1a71093ae01ea7347e3..82dca8f80bf170fde5705862c3eeb9d994725042 100644
GIT binary patch
delta 2524
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
delta 7
OcmbOxdzEv;RTcmY+5;N^
diff --git a/t/lib-gpg/trustdb.gpg b/t/lib-gpg/trustdb.gpg
index abace962b8bf84be688a6f27e4ebd0ee7052f210..4879ae9a84650a93a4d15bd6560c5d1b89eb4c2f 100644
GIT binary patch
delta 133
zcmZqRy1*sEm|l?1%*@Ej$i%=9=re5@0|Nu&L_y(=>YJDu6*k{umSl_t3LyXw!<BtX
zhEkV><>GE>E=lCnYu&C?*vSl0pomb%%dj-dsEA)Mq*Svt$mH(j_twWhW_@KtD1fp6
DE~Fa=
delta 52
zcmcb>)xagdm|l?1%*@Ej$iTqhmVVlAqM`Uk^-atZ9rz~(oS3|U#hLd&3{VON0Ad;p
ADF6Tf
diff --git a/t/t7612-merge-verify-signatures.sh b/t/t7612-merge-verify-signatures.sh
new file mode 100755
index 0000000..47a4f80
--- /dev/null
+++ b/t/t7612-merge-verify-signatures.sh
@@ -0,0 +1,61 @@
+#!/bin/sh
+
+test_description='merge signature verification tests'
+. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-gpg.sh"
+
+test_expect_success GPG 'create signed commits' '
+ echo 1 >file && git add file &&
+ test_tick && git commit -m initial &&
+ git tag initial &&
+
+ git checkout -b side-signed &&
+ echo 3 >elif && git add elif &&
+ test_tick && git commit -S -m "signed on side" &&
+ git checkout initial &&
+
+ git checkout -b side-unsigned &&
+ echo 3 >foo && git add foo &&
+ test_tick && git commit -m "unsigned on side" &&
+ git checkout initial &&
+
+ git checkout -b side-bad &&
+ echo 3 >bar && git add bar &&
+ test_tick && git commit -S -m "bad on side" &&
+ git cat-file commit side-bad >raw &&
+ sed -e "s/bad/forged bad/" raw >forged &&
+ git hash-object -w -t commit forged >forged.commit &&
+ git checkout initial &&
+
+ git checkout -b side-untrusted &&
+ echo 3 >baz && git add baz &&
+ test_tick && git commit -SB7227189 -m "untrusted on side"
+
+ git checkout master
+'
+
+test_expect_success GPG 'merge unsigned commit with verification' '
+ test_must_fail git merge --ff-only --verify-signatures side-unsigned 2> mergeerror &&
+ grep "does not have a GPG signature" mergeerror
+'
+
+test_expect_success GPG 'merge commit with bad signature with verification' '
+ test_must_fail git merge --ff-only --verify-signatures $(cat forged.commit) 2> mergeerror &&
+ grep "has a bad GPG signature" mergeerror
+'
+
+test_expect_success GPG 'merge commit with untrusted signature with verification' '
+ test_must_fail git merge --ff-only --verify-signatures side-untrusted 2> mergeerror &&
+ grep "from an untrusted key" mergeerror
+'
+
+test_expect_success GPG 'merge signed commit with verification' '
+ git merge -v --ff-only --verify-signatures side-signed > mergeoutput &&
+ grep "has a good GPG signature" mergeoutput
+'
+
+test_expect_success GPG 'merge commit with bad signature without verification' '
+ git merge $(cat forged.commit)
+'
+
+test_done
--
1.8.1.5
next reply other threads:[~2013-03-22 22:12 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-03-22 21:51 Sebastian Götte [this message]
2013-03-22 23:02 ` [PATCH] merge/pull: verify GPG signatures of commits being merged Junio C Hamano
2013-03-22 23:14 ` Jonathan Nieder
2013-03-23 3:08 ` Junio C Hamano
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=514CD26C.2070702@physik.tu-berlin.de \
--to=jaseg@physik$(echo .)tu-berlin.de \
--cc=git@vger$(echo .)kernel.org \
--cc=gitster@pobox$(echo .)com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox