fsck: consider gpgsig headers expected in tags

When we're creating a tag, we want to make sure that gpgsig and gpgsig-sha256 headers are allowed for the commit. The default fsck behavior is to ignore the fact that they're left over, but some of our tests enable strict checking which flags them nonetheless. Add improved checking for these headers as well as documentation and several tests. Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

brian m. carlson committed Oct 9, 2025 at 21:56 UTC 51acda73d3ca96b763f0fca3d7b33b4beaef786d
4 files changed +80
Documentation/fsck-msgids.adoc
+6
@@ -10,6 +10,12 @@
10 `badFilemode`::
11 (INFO) A tree contains a bad filemode entry.
12
13 +`badGpgsig`::
14 + (ERROR) A tag contains a bad (truncated) signature (e.g., `gpgsig`) header.
15 +
16 +`badHeaderContinuation`::
17 + (ERROR) A continuation header (such as for `gpgsig`) is unexpectedly truncated.
18 +
19 `badName`::
20 (ERROR) An author/committer name is empty.
21
fsck.c
+18
@@ -1067,6 +1067,24 @@ int fsck_tag_standalone(const struct object_id *oid, const char *buffer,
1067 else
1068 ret = fsck_ident(&buffer, oid, OBJ_TAG, options);
1069
1070 + if (buffer < buffer_end && (skip_prefix(buffer, "gpgsig ", &buffer) || skip_prefix(buffer, "gpgsig-sha256 ", &buffer))) {
1071 + eol = memchr(buffer, '\n', buffer_end - buffer);
1072 + if (!eol) {
1073 + ret = report(options, oid, OBJ_TAG, FSCK_MSG_BAD_GPGSIG, "invalid format - unexpected end after 'gpgsig' or 'gpgsig-sha256' line");
1074 + goto done;
1075 + }
1076 + buffer = eol + 1;
1077 +
1078 + while (buffer < buffer_end && starts_with(buffer, " ")) {
1079 + eol = memchr(buffer, '\n', buffer_end - buffer);
1080 + if (!eol) {
1081 + ret = report(options, oid, OBJ_TAG, FSCK_MSG_BAD_HEADER_CONTINUATION, "invalid format - unexpected end in 'gpgsig' or 'gpgsig-sha256' continuation line");
1082 + goto done;
1083 + }
1084 + buffer = eol + 1;
1085 + }
1086 + }
1087 +
1088 if (buffer < buffer_end && !starts_with(buffer, "\n")) {
1089 /*
1090 * The verify_headers() check will allow
fsck.h
+2
@@ -25,9 +25,11 @@ enum fsck_msg_type {
25 FUNC(NUL_IN_HEADER, FATAL) \
26 FUNC(UNTERMINATED_HEADER, FATAL) \
27 /* errors */ \
28 + FUNC(BAD_HEADER_CONTINUATION, ERROR) \
29 FUNC(BAD_DATE, ERROR) \
30 FUNC(BAD_DATE_OVERFLOW, ERROR) \
31 FUNC(BAD_EMAIL, ERROR) \
32 + FUNC(BAD_GPGSIG, ERROR) \
33 FUNC(BAD_NAME, ERROR) \
34 FUNC(BAD_OBJECT_SHA1, ERROR) \
35 FUNC(BAD_PACKED_REF_ENTRY, ERROR) \
t/t1450-fsck.sh
+54
@@ -454,6 +454,60 @@ test_expect_success 'tag with NUL in header' '
454 test_grep "error in tag $tag.*unterminated header: NUL at offset" out
455 '
456
457 +test_expect_success 'tag accepts gpgsig header even if not validly signed' '
458 + test_oid_cache <<-\EOF &&
459 + header sha1:gpgsig-sha256
460 + header sha256:gpgsig
461 + EOF
462 + header=$(test_oid header) &&
463 + sha=$(git rev-parse HEAD) &&
464 + cat >good-tag <<-EOF &&
465 + object $sha
466 + type commit
467 + tag good
468 + tagger T A Gger <tagger@example.com> 1234567890 -0000
469 + $header -----BEGIN PGP SIGNATURE-----
470 + Not a valid signature
471 + -----END PGP SIGNATURE-----
472 +
473 + This is a good tag.
474 + EOF
475 +
476 + tag=$(git hash-object --literally -t tag -w --stdin <good-tag) &&
477 + test_when_finished "remove_object $tag" &&
478 + git update-ref refs/tags/good $tag &&
479 + test_when_finished "git update-ref -d refs/tags/good" &&
480 + git -c fsck.extraHeaderEntry=error fsck --tags
481 +'
482 +
483 +test_expect_success 'tag rejects invalid headers' '
484 + test_oid_cache <<-\EOF &&
485 + header sha1:gpgsig-sha256
486 + header sha256:gpgsig
487 + EOF
488 + header=$(test_oid header) &&
489 + sha=$(git rev-parse HEAD) &&
490 + cat >bad-tag <<-EOF &&
491 + object $sha
492 + type commit
493 + tag good
494 + tagger T A Gger <tagger@example.com> 1234567890 -0000
495 + $header -----BEGIN PGP SIGNATURE-----
496 + Not a valid signature
497 + -----END PGP SIGNATURE-----
498 + junk
499 +
500 + This is a bad tag with junk at the end of the headers.
501 + EOF
502 +
503 + tag=$(git hash-object --literally -t tag -w --stdin <bad-tag) &&
504 + test_when_finished "remove_object $tag" &&
505 + git update-ref refs/tags/bad $tag &&
506 + test_when_finished "git update-ref -d refs/tags/bad" &&
507 + test_must_fail git -c fsck.extraHeaderEntry=error fsck --tags 2>out &&
508 + test_grep "error in tag $tag.*invalid format - extra header" out
509 +'
510 +
511 test_expect_success 'cleaned up' '
512 git fsck >actual 2>&1 &&
513 test_must_be_empty actual