Skip to content

Commit 817b042

Browse files
jltoblergitster
authored andcommitted
fast-import: add 'strip-if-invalid' mode to '--signed-tags=<mode>'
With c20f112 (fast-import: add 'strip-if-invalid' mode to --signed-commits=<mode>, 2025-11-17), git-fast-import(1) learned to verify commit signatures during import and strip signatures that fail verification. Extend the same behavior to signed tag objects by introducing a 'strip-if-invalid' mode for the '--signed-tags' option. Signed-off-by: Justin Tobler <jltobler@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 4c36345 commit 817b042

File tree

3 files changed

+110
-10
lines changed

3 files changed

+110
-10
lines changed

Documentation/git-fast-import.adoc

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,10 @@ fast-import stream! This option is enabled automatically for
6666
remote-helpers that use the `import` capability, as they are
6767
already trusted to run their own code.
6868

69-
`--signed-tags=(verbatim|warn-verbatim|warn-strip|strip|abort)`::
69+
`--signed-tags=<mode>`::
7070
Specify how to handle signed tags. Behaves in the same way as
71-
the `--signed-commits=<mode>` below, except that the
72-
`strip-if-invalid` mode is not yet supported. Like for signed
73-
commits, the default mode is `verbatim`.
71+
the `--signed-commits=<mode>` below. Like for signed commits,
72+
the default mode is `verbatim`.
7473

7574
`--signed-commits=<mode>`::
7675
Specify how to handle signed commits. The following <mode>s

builtin/fast-import.c

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3089,7 +3089,34 @@ static void parse_new_commit(const char *arg)
30893089
b->last_commit = object_count_by_type[OBJ_COMMIT];
30903090
}
30913091

3092-
static void handle_tag_signature(struct strbuf *msg, const char *name)
3092+
static void handle_tag_signature_if_invalid(struct strbuf *buf,
3093+
struct strbuf *msg,
3094+
size_t sig_offset)
3095+
{
3096+
struct strbuf signature = STRBUF_INIT;
3097+
struct strbuf payload = STRBUF_INIT;
3098+
struct signature_check sigc = { 0 };
3099+
3100+
strbuf_addbuf(&payload, buf);
3101+
strbuf_addch(&payload, '\n');
3102+
strbuf_add(&payload, msg->buf, sig_offset);
3103+
strbuf_add(&signature, msg->buf + sig_offset, msg->len - sig_offset);
3104+
3105+
sigc.payload_type = SIGNATURE_PAYLOAD_TAG;
3106+
sigc.payload = strbuf_detach(&payload, &sigc.payload_len);
3107+
3108+
if (!check_signature(&sigc, signature.buf, signature.len))
3109+
goto out;
3110+
3111+
strbuf_setlen(msg, sig_offset);
3112+
3113+
out:
3114+
signature_check_clear(&sigc);
3115+
strbuf_release(&signature);
3116+
strbuf_release(&payload);
3117+
}
3118+
3119+
static void handle_tag_signature(struct strbuf *buf, struct strbuf *msg, const char *name)
30933120
{
30943121
size_t sig_offset = parse_signed_buffer(msg->buf, msg->len);
30953122

@@ -3115,6 +3142,9 @@ static void handle_tag_signature(struct strbuf *msg, const char *name)
31153142
/* Truncate the buffer to remove the signature */
31163143
strbuf_setlen(msg, sig_offset);
31173144
break;
3145+
case SIGN_STRIP_IF_INVALID:
3146+
handle_tag_signature_if_invalid(buf, msg, sig_offset);
3147+
break;
31183148

31193149
/* Third, aborting modes */
31203150
case SIGN_ABORT:
@@ -3123,9 +3153,6 @@ static void handle_tag_signature(struct strbuf *msg, const char *name)
31233153
case SIGN_ABORT_IF_INVALID:
31243154
die(_("'abort-if-invalid' is not a valid mode for "
31253155
"git fast-import with --signed-tags=<mode>"));
3126-
case SIGN_STRIP_IF_INVALID:
3127-
die(_("'strip-if-invalid' is not a valid mode for "
3128-
"git fast-import with --signed-tags=<mode>"));
31293156
case SIGN_SIGN_IF_INVALID:
31303157
die(_("'sign-if-invalid' is not a valid mode for "
31313158
"git fast-import with --signed-tags=<mode>"));
@@ -3198,8 +3225,6 @@ static void parse_new_tag(const char *arg)
31983225
/* tag payload/message */
31993226
parse_data(&msg, 0, NULL);
32003227

3201-
handle_tag_signature(&msg, t->name);
3202-
32033228
/* build the tag object */
32043229
strbuf_reset(&new_data);
32053230

@@ -3211,6 +3236,9 @@ static void parse_new_tag(const char *arg)
32113236
if (tagger)
32123237
strbuf_addf(&new_data,
32133238
"tagger %s\n", tagger);
3239+
3240+
handle_tag_signature(&new_data, &msg, t->name);
3241+
32143242
strbuf_addch(&new_data, '\n');
32153243
strbuf_addbuf(&new_data, &msg);
32163244
free(tagger);

t/t9306-fast-import-signed-tags.sh

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,77 @@ test_expect_success GPGSSH 'import SSH signed tag with --signed-tags=strip' '
7777
test_grep ! "SSH SIGNATURE" out
7878
'
7979

80+
for mode in strip-if-invalid
81+
do
82+
test_expect_success GPG "import tag with no signature with --signed-tags=$mode" '
83+
test_when_finished rm -rf import &&
84+
git init import &&
85+
86+
git fast-export --signed-tags=verbatim >output &&
87+
git -C import fast-import --quiet --signed-tags=$mode <output >log 2>&1 &&
88+
test_must_be_empty log
89+
'
90+
91+
test_expect_success GPG "keep valid OpenPGP signature with --signed-tags=$mode" '
92+
test_when_finished rm -rf import &&
93+
git init import &&
94+
95+
git fast-export --signed-tags=verbatim openpgp-signed >output &&
96+
git -C import fast-import --quiet --signed-tags=$mode <output >log 2>&1 &&
97+
IMPORTED=$(git -C import rev-parse --verify refs/tags/openpgp-signed) &&
98+
test $OPENPGP_SIGNED = $IMPORTED &&
99+
git -C import cat-file tag "$IMPORTED" >actual &&
100+
test_grep -E "^-----BEGIN PGP SIGNATURE-----" actual &&
101+
test_must_be_empty log
102+
'
103+
104+
test_expect_success GPG "handle signature invalidated by message change with --signed-tags=$mode" '
105+
test_when_finished rm -rf import &&
106+
git init import &&
107+
108+
git fast-export --signed-tags=verbatim openpgp-signed >output &&
109+
110+
# Change the tag message, which invalidates the signature. The tag
111+
# message length should not change though, otherwise the corresponding
112+
# `data <length>` command would have to be changed too.
113+
sed "s/OpenPGP signed tag/OpenPGP forged tag/" output >modified &&
114+
115+
git -C import fast-import --quiet --signed-tags=$mode <modified >log 2>&1 &&
116+
117+
IMPORTED=$(git -C import rev-parse --verify refs/tags/openpgp-signed) &&
118+
test $OPENPGP_SIGNED != $IMPORTED &&
119+
git -C import cat-file tag "$IMPORTED" >actual &&
120+
test_grep ! -E "^-----BEGIN PGP SIGNATURE-----" actual &&
121+
test_must_be_empty log
122+
'
123+
124+
test_expect_success GPGSM "keep valid X.509 signature with --signed-tags=$mode" '
125+
test_when_finished rm -rf import &&
126+
git init import &&
127+
128+
git fast-export --signed-tags=verbatim x509-signed >output &&
129+
git -C import fast-import --quiet --signed-tags=$mode <output >log 2>&1 &&
130+
IMPORTED=$(git -C import rev-parse --verify refs/tags/x509-signed) &&
131+
test $X509_SIGNED = $IMPORTED &&
132+
git -C import cat-file tag x509-signed >actual &&
133+
test_grep -E "^-----BEGIN SIGNED MESSAGE-----" actual &&
134+
test_must_be_empty log
135+
'
136+
137+
test_expect_success GPGSSH "keep valid SSH signature with --signed-tags=$mode" '
138+
test_when_finished rm -rf import &&
139+
git init import &&
140+
141+
test_config -C import gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
142+
143+
git fast-export --signed-tags=verbatim ssh-signed >output &&
144+
git -C import fast-import --quiet --signed-tags=$mode <output >log 2>&1 &&
145+
IMPORTED=$(git -C import rev-parse --verify refs/tags/ssh-signed) &&
146+
test $SSH_SIGNED = $IMPORTED &&
147+
git -C import cat-file tag ssh-signed >actual &&
148+
test_grep -E "^-----BEGIN SSH SIGNATURE-----" actual &&
149+
test_must_be_empty log
150+
'
151+
done
152+
80153
test_done

0 commit comments

Comments
 (0)