git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v4 3/5] merge/pull: verify GPG signatures of commits being merged

From
SGSebastian Götte <jaseg@physik.tu-berlin.de>
Date
Mar 26, 2013, 11:05 UTC
Message-ID
<5151810A.1010203@physik.tu-berlin.de>
In-Reply-To
<cover.1364295502.git.jaseg@physik-pool.tu-berlin.de>

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.

Signed-off-by: Sebastian Götte <jaseg@physik-pool.tu-berlin.de>
---
 Documentation/merge-options.txt    |  4 +++
 builtin/merge.c                    | 33 +++++++++++++++++++++++-
 git-pull.sh                        | 10 ++++++--
 t/t7612-merge-verify-signatures.sh | 52 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 96 insertions(+), 3 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/builtin/merge.c b/builtin/merge.c
index 7c8922c..227040b 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;
 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 (key fingerprint %s)\n"), hex, signature.signer, signature.key);
+					break;
+				case 'B':
+					die(_("Commit %s has a bad GPG signature allegedly by %s (key fingerprint %s)."), hex, signature.signer, signature.key);
+				default: /* 'N' */
+					die(_("Commit %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.gpg_status);
+			free(signature.signer);
+			free(signature.key);
+		}
+	}
+
 	strbuf_addstr(&buf, "merge");
 	for (p = remoteheads; p; p = p->next)
 		strbuf_addf(&buf, " %s", merge_remote_util(p->item)->name);
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/t/t7612-merge-verify-signatures.sh b/t/t7612-merge-verify-signatures.sh
new file mode 100755
index 0000000..31b67dd
--- /dev/null
+++ b/t/t7612-merge-verify-signatures.sh
@@ -0,0 +1,52 @@
+#!/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 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 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
Previous: Junio C HamanoNext: Junio C Hamano
Message 16 of 62 in “Move commit GPG signature verification to commit.c”
  1. 1/4 Move commit GPG signature verification to commit.cSebastian Götte, Mar 23, 2013
  2. Junio C HamanoMar 25, 2013
  3. 0/5 Verify GPG signatures when merging and extend %G? pretty stringSebastian Götte, Mar 25, 2013
  4. Junio C HamanoMar 26, 2013
  5. 0/5 Verify GPG signatures when merging and extend %G? pretty stringSebastian Götte, Mar 26, 2013
  6. Junio C HamanoMar 26, 2013
  7. Sebastian GötteMar 26, 2013
  8. 1/5 Move commit GPG signature verification to commit.cSebastian Götte, Mar 25, 2013
  9. 2/5 commit.c/GPG signature verification: Also look at the first GPG status lineSebastian Götte, Mar 25, 2013
  10. 3/5 merge/pull: verify GPG signatures of commits being mergedSebastian Götte, Mar 25, 2013
  11. 4/5 merge/pull Check for untrusted good GPG signaturesSebastian Götte, Mar 25, 2013
  12. 5/5 pretty printing: extend %G? to include 'N' and 'U'Sebastian Götte, Mar 25, 2013
  13. 1/5 Move commit GPG signature verification to commit.cSebastian Götte, Mar 26, 2013
  14. 2/5 commit.c/GPG signature verification: Also look at the first GPG status lineSebastian Götte, Mar 26, 2013
  15. Junio C HamanoMar 28, 2013
  16. 3/5 merge/pull: verify GPG signatures of commits being mergedSebastian Götte, Mar 26, 2013
  17. Junio C HamanoMar 28, 2013
  18. 0/5 Verify GPG signatures when merging and extend %G? pretty stringSebastian Götte, Mar 30, 2013
  19. 4/5 merge/pull Check for untrusted good GPG signaturesSebastian Götte, Mar 26, 2013
  20. 5/5 pretty printing: extend %G? to include 'N' and 'U'Sebastian Götte, Mar 26, 2013
  21. 1/5 Move commit GPG signature verification to commit.cSebastian Götte, Mar 30, 2013
  22. Junio C HamanoMar 30, 2013
  23. 2/5 commit.c/GPG signature verification: Also look at the first GPG status lineSebastian Götte, Mar 30, 2013
  24. Junio C HamanoMar 30, 2013
  25. 3/5 merge/pull: verify GPG signatures of commits being mergedSebastian Götte, Mar 30, 2013
  26. Junio C HamanoMar 30, 2013
  27. 0/5 Verify GPG signatures when merging and extend %G? pretty stringSebastian Götte, Mar 30, 2013
  28. 4/5 merge/pull Check for untrusted good GPG signaturesSebastian Götte, Mar 30, 2013
  29. Thomas RastMar 31, 2013
  30. Sebastian GötteMar 31, 2013
  31. Thomas RastMar 31, 2013
  32. Sebastian GötteMar 31, 2013
  33. Thomas RastMar 31, 2013
  34. Sebastian GötteMar 31, 2013
  35. John KeepingMar 31, 2013
  36. 0/5 Verify GPG signatures when merging and extend %G? pretty stringSebastian Götte, Mar 31, 2013
  37. 5/5 pretty printing: extend %G? to include 'N' and 'U'Sebastian Götte, Mar 30, 2013
  38. 1/5 Move commit GPG signature verification to commit.cSebastian Götte, Mar 30, 2013
  39. 2/5 commit.c/GPG signature verification: Also look at the first GPG status lineSebastian Götte, Mar 30, 2013
  40. 3/5 merge/pull: verify GPG signatures of commits being mergedSebastian Götte, Mar 30, 2013
  41. 4/5 merge/pull Check for untrusted good GPG signaturesSebastian Götte, Mar 30, 2013
  42. 5/5 pretty printing: extend %G? to include 'N' and 'U'Sebastian Götte, Mar 30, 2013
  43. 1/5 Move commit GPG signature verification to commit.cSebastian Götte, Mar 31, 2013
  44. 2/5 commit.c/GPG signature verification: Also look at the first GPG status lineSebastian Götte, Mar 31, 2013
  45. John KeepingMar 31, 2013
  46. 3/5 merge/pull: verify GPG signatures of commits being mergedSebastian Götte, Mar 31, 2013
  47. 4/5 merge/pull Check for untrusted good GPG signaturesSebastian Götte, Mar 31, 2013
  48. John KeepingMar 31, 2013
  49. Thomas RastMar 31, 2013
  50. Sebastian GötteMar 31, 2013
  51. Thomas RastMar 31, 2013
  52. John KeepingMar 31, 2013
  53. 0/5 Verify GPG signatures when merging and extend %G? pretty stringSebastian Götte, Mar 31, 2013
  54. 5/5 pretty printing: extend %G? to include 'N' and 'U'Sebastian Götte, Mar 31, 2013
  55. 1/5 Move commit GPG signature verification to commit.cSebastian Götte, Mar 31, 2013
  56. 2/5 commit.c/GPG signature verification: Also look at the first GPG status lineSebastian Götte, Mar 31, 2013
  57. 3/5 merge/pull: verify GPG signatures of commits being mergedSebastian Götte, Mar 31, 2013
  58. Junio C HamanoApr 1, 2013
  59. Sebastian GötteApr 1, 2013
  60. Junio C HamanoApr 1, 2013
  61. 4/5 merge/pull Check for untrusted good GPG signaturesSebastian Götte, Mar 31, 2013
  62. 5/5 pretty printing: extend %G? to include 'N' and 'U'Sebastian Götte, Mar 31, 2013

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.