Skip to content

Commit 7a15334

Browse files
lunnywxiaoguang
andauthored
Fix git commit committer parsing and add some tests (#35007)
* Fix #34991 * Fix #34882 --------- Co-authored-by: wxiaoguang <[email protected]>
1 parent a5a3d9b commit 7a15334

File tree

12 files changed

+93
-106
lines changed

12 files changed

+93
-106
lines changed

Dockerfile

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ RUN chmod 755 /tmp/local/usr/bin/entrypoint \
3939
/tmp/local/etc/s6/.s6-svscan/* \
4040
/go/src/code.gitea.io/gitea/gitea \
4141
/go/src/code.gitea.io/gitea/environment-to-ini
42-
RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete
4342

4443
FROM docker.io/library/alpine:3.22
4544
LABEL maintainer="[email protected]"
@@ -83,4 +82,3 @@ CMD ["/usr/bin/s6-svscan", "/etc/s6"]
8382
COPY --from=build-env /tmp/local /
8483
COPY --from=build-env /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea
8584
COPY --from=build-env /go/src/code.gitea.io/gitea/environment-to-ini /usr/local/bin/environment-to-ini
86-
COPY --from=build-env /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete /etc/profile.d/gitea_bash_autocomplete.sh

Dockerfile.rootless

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ RUN chmod 755 /tmp/local/usr/local/bin/docker-entrypoint.sh \
3737
/tmp/local/usr/local/bin/gitea \
3838
/go/src/code.gitea.io/gitea/gitea \
3939
/go/src/code.gitea.io/gitea/environment-to-ini
40-
RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete
4140

4241
FROM docker.io/library/alpine:3.22
4342
LABEL maintainer="[email protected]"
@@ -72,7 +71,6 @@ RUN chown git:git /var/lib/gitea /etc/gitea
7271
COPY --from=build-env /tmp/local /
7372
COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea
7473
COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/environment-to-ini /usr/local/bin/environment-to-ini
75-
COPY --from=build-env /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete /etc/profile.d/gitea_bash_autocomplete.sh
7674

7775
# git:git
7876
USER 1000:1000

models/asymkey/gpg_key_commit_verification.go

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,34 +15,15 @@ import (
1515
"github.com/ProtonMail/go-crypto/openpgp/packet"
1616
)
1717

18-
// __________________ ________ ____ __.
19-
// / _____/\______ \/ _____/ | |/ _|____ ___.__.
20-
// / \ ___ | ___/ \ ___ | <_/ __ < | |
21-
// \ \_\ \| | \ \_\ \ | | \ ___/\___ |
22-
// \______ /|____| \______ / |____|__ \___ > ____|
23-
// \/ \/ \/ \/\/
24-
// _________ .__ __
25-
// \_ ___ \ ____ _____ _____ |__|/ |_
26-
// / \ \/ / _ \ / \ / \| \ __\
27-
// \ \___( <_> ) Y Y \ Y Y \ || |
28-
// \______ /\____/|__|_| /__|_| /__||__|
29-
// \/ \/ \/
30-
// ____ ____ .__ _____.__ __ .__
31-
// \ \ / /___________|__|/ ____\__| ____ _____ _/ |_|__| ____ ____
32-
// \ Y // __ \_ __ \ \ __\| |/ ___\\__ \\ __\ |/ _ \ / \
33-
// \ /\ ___/| | \/ || | | \ \___ / __ \| | | ( <_> ) | \
34-
// \___/ \___ >__| |__||__| |__|\___ >____ /__| |__|\____/|___| /
35-
// \/ \/ \/ \/
36-
3718
// This file provides functions relating commit verification
3819

3920
// CommitVerification represents a commit validation of signature
4021
type CommitVerification struct {
4122
Verified bool
4223
Warning bool
4324
Reason string
44-
SigningUser *user_model.User
45-
CommittingUser *user_model.User
25+
SigningUser *user_model.User // if Verified, then SigningUser is non-nil
26+
CommittingUser *user_model.User // if Verified, then CommittingUser is non-nil
4627
SigningEmail string
4728
SigningKey *GPGKey
4829
SigningSSHKey *PublicKey

models/user/user.go

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1166,12 +1166,6 @@ func ValidateCommitsWithEmails(ctx context.Context, oldCommits []*git.Commit) ([
11661166

11671167
for _, c := range oldCommits {
11681168
user := emailUserMap.GetByEmail(c.Author.Email) // FIXME: why ValidateCommitsWithEmails uses "Author", but ParseCommitsWithSignature uses "Committer"?
1169-
if user == nil {
1170-
user = &User{
1171-
Name: c.Author.Name,
1172-
Email: c.Author.Email,
1173-
}
1174-
}
11751169
newCommits = append(newCommits, &UserCommit{
11761170
User: user,
11771171
Commit: c,
@@ -1195,12 +1189,14 @@ func GetUsersByEmails(ctx context.Context, emails []string) (*EmailUserMap, erro
11951189

11961190
needCheckEmails := make(container.Set[string])
11971191
needCheckUserNames := make(container.Set[string])
1192+
noReplyAddressSuffix := "@" + strings.ToLower(setting.Service.NoReplyAddress)
11981193
for _, email := range emails {
1199-
if strings.HasSuffix(email, "@"+setting.Service.NoReplyAddress) {
1200-
username := strings.TrimSuffix(email, "@"+setting.Service.NoReplyAddress)
1201-
needCheckUserNames.Add(strings.ToLower(username))
1194+
emailLower := strings.ToLower(email)
1195+
if noReplyUserNameLower, ok := strings.CutSuffix(emailLower, noReplyAddressSuffix); ok {
1196+
needCheckUserNames.Add(noReplyUserNameLower)
1197+
needCheckEmails.Add(emailLower)
12021198
} else {
1203-
needCheckEmails.Add(strings.ToLower(email))
1199+
needCheckEmails.Add(emailLower)
12041200
}
12051201
}
12061202

models/user/user_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,11 @@ func TestUserEmails(t *testing.T) {
8585
testGetUserByEmail(t, c.Email, c.UID)
8686
})
8787
}
88+
89+
t.Run("NoReplyConflict", func(t *testing.T) {
90+
setting.Service.NoReplyAddress = "example.com"
91+
testGetUserByEmail(t, "[email protected]", 1)
92+
})
8893
})
8994
}
9095

modules/git/commit.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ import (
2222
type Commit struct {
2323
Tree // FIXME: bad design, this field can be nil if the commit is from "last commit cache"
2424

25-
ID ObjectID // The ID of this commit object
26-
Author *Signature
27-
Committer *Signature
25+
ID ObjectID
26+
Author *Signature // never nil
27+
Committer *Signature // never nil
2828
CommitMessage string
2929
Signature *CommitSignature
3030

services/asymkey/commit.go

Lines changed: 28 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -24,47 +24,43 @@ import (
2424

2525
// ParseCommitWithSignature check if signature is good against keystore.
2626
func ParseCommitWithSignature(ctx context.Context, c *git.Commit) *asymkey_model.CommitVerification {
27-
var committer *user_model.User
28-
if c.Committer != nil {
29-
var err error
30-
// Find Committer account
31-
committer, err = user_model.GetUserByEmail(ctx, c.Committer.Email) // This finds the user by primary email or activated email so commit will not be valid if email is not
32-
if err != nil { // Skipping not user for committer
33-
committer = &user_model.User{
34-
Name: c.Committer.Name,
35-
Email: c.Committer.Email,
36-
}
37-
// We can expect this to often be an ErrUserNotExist. in the case
38-
// it is not, however, it is important to log it.
39-
if !user_model.IsErrUserNotExist(err) {
40-
log.Error("GetUserByEmail: %v", err)
41-
return &asymkey_model.CommitVerification{
42-
CommittingUser: committer,
43-
Verified: false,
44-
Reason: "gpg.error.no_committer_account",
45-
}
46-
}
27+
committer, err := user_model.GetUserByEmail(ctx, c.Committer.Email)
28+
if err != nil && !user_model.IsErrUserNotExist(err) {
29+
log.Error("GetUserByEmail: %v", err)
30+
return &asymkey_model.CommitVerification{
31+
Verified: false,
32+
Reason: "gpg.error.no_committer_account", // this error is not right, but such error should seldom happen
4733
}
4834
}
49-
5035
return ParseCommitWithSignatureCommitter(ctx, c, committer)
5136
}
5237

38+
// ParseCommitWithSignatureCommitter parses a commit's GPG or SSH signature.
39+
// If the commit is singed by an instance key, then committer can be nil.
40+
// If the signature exists, even if committer is nil, the returned CommittingUser will be a non-nil fake user.
5341
func ParseCommitWithSignatureCommitter(ctx context.Context, c *git.Commit, committer *user_model.User) *asymkey_model.CommitVerification {
54-
// If no signature just report the committer
42+
// If no signature, just report the committer
5543
if c.Signature == nil {
5644
return &asymkey_model.CommitVerification{
5745
CommittingUser: committer,
58-
Verified: false, // Default value
59-
Reason: "gpg.error.not_signed_commit", // Default value
46+
Verified: false,
47+
Reason: "gpg.error.not_signed_commit",
48+
}
49+
}
50+
// to support instance key, we need a fake committer user (not really needed, but legacy code accesses the committer without nil-check)
51+
if committer == nil {
52+
committer = &user_model.User{
53+
Name: c.Committer.Name,
54+
Email: c.Committer.Email,
6055
}
6156
}
62-
63-
// If this a SSH signature handle it differently
6457
if strings.HasPrefix(c.Signature.Signature, "-----BEGIN SSH SIGNATURE-----") {
65-
return ParseCommitWithSSHSignature(ctx, c, committer)
58+
return parseCommitWithSSHSignature(ctx, c, committer)
6659
}
60+
return parseCommitWithGPGSignature(ctx, c, committer)
61+
}
6762

63+
func parseCommitWithGPGSignature(ctx context.Context, c *git.Commit, committer *user_model.User) *asymkey_model.CommitVerification {
6864
// Parsing signature
6965
sig, err := asymkey_model.ExtractSignature(c.Signature.Signature)
7066
if err != nil { // Skipping failed to extract sign
@@ -165,7 +161,7 @@ func ParseCommitWithSignatureCommitter(ctx context.Context, c *git.Commit, commi
165161
}
166162
if err := gpgSettings.LoadPublicKeyContent(); err != nil {
167163
log.Error("Error getting default signing key: %s %v", gpgSettings.KeyID, err)
168-
} else if commitVerification := VerifyWithGPGSettings(ctx, &gpgSettings, sig, c.Signature.Payload, committer, keyID); commitVerification != nil {
164+
} else if commitVerification := verifyWithGPGSettings(ctx, &gpgSettings, sig, c.Signature.Payload, committer, keyID); commitVerification != nil {
169165
if commitVerification.Reason == asymkey_model.BadSignature {
170166
defaultReason = asymkey_model.BadSignature
171167
} else {
@@ -180,7 +176,7 @@ func ParseCommitWithSignatureCommitter(ctx context.Context, c *git.Commit, commi
180176
} else if defaultGPGSettings == nil {
181177
log.Warn("Unable to get defaultGPGSettings for unattached commit: %s", c.ID.String())
182178
} else if defaultGPGSettings.Sign {
183-
if commitVerification := VerifyWithGPGSettings(ctx, defaultGPGSettings, sig, c.Signature.Payload, committer, keyID); commitVerification != nil {
179+
if commitVerification := verifyWithGPGSettings(ctx, defaultGPGSettings, sig, c.Signature.Payload, committer, keyID); commitVerification != nil {
184180
if commitVerification.Reason == asymkey_model.BadSignature {
185181
defaultReason = asymkey_model.BadSignature
186182
} else {
@@ -295,7 +291,7 @@ func HashAndVerifyForKeyID(ctx context.Context, sig *packet.Signature, payload s
295291
}
296292
}
297293

298-
func VerifyWithGPGSettings(ctx context.Context, gpgSettings *git.GPGSettings, sig *packet.Signature, payload string, committer *user_model.User, keyID string) *asymkey_model.CommitVerification {
294+
func verifyWithGPGSettings(ctx context.Context, gpgSettings *git.GPGSettings, sig *packet.Signature, payload string, committer *user_model.User, keyID string) *asymkey_model.CommitVerification {
299295
// First try to find the key in the db
300296
if commitVerification := HashAndVerifyForKeyID(ctx, sig, payload, committer, gpgSettings.KeyID, gpgSettings.Name, gpgSettings.Email); commitVerification != nil {
301297
return commitVerification
@@ -375,8 +371,8 @@ func verifySSHCommitVerificationByInstanceKey(c *git.Commit, committerUser, sign
375371
return verifySSHCommitVerification(c.Signature.Signature, c.Signature.Payload, sshPubKey, committerUser, signerUser, committerGitEmail)
376372
}
377373

378-
// ParseCommitWithSSHSignature check if signature is good against keystore.
379-
func ParseCommitWithSSHSignature(ctx context.Context, c *git.Commit, committerUser *user_model.User) *asymkey_model.CommitVerification {
374+
// parseCommitWithSSHSignature check if signature is good against keystore.
375+
func parseCommitWithSSHSignature(ctx context.Context, c *git.Commit, committerUser *user_model.User) *asymkey_model.CommitVerification {
380376
// Now try to associate the signature with the committer, if present
381377
if committerUser.ID != 0 {
382378
keys, err := db.Find[asymkey_model.PublicKey](ctx, asymkey_model.FindPublicKeyOptions{

services/asymkey/commit_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ Initial commit with signed file
4141
Name: "User Two",
4242
4343
}
44-
ret := ParseCommitWithSSHSignature(t.Context(), commit, committingUser)
44+
ret := parseCommitWithSSHSignature(t.Context(), commit, committingUser)
4545
require.NotNil(t, ret)
4646
assert.True(t, ret.Verified)
4747
assert.False(t, ret.Warning)

services/git/commit.go

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,6 @@ func ParseCommitsWithSignature(ctx context.Context, repo *repo_model.Repository,
3535

3636
for _, c := range oldCommits {
3737
committerUser := emailUsers.GetByEmail(c.Committer.Email) // FIXME: why ValidateCommitsWithEmails uses "Author", but ParseCommitsWithSignature uses "Committer"?
38-
if committerUser == nil {
39-
committerUser = &user_model.User{
40-
Name: c.Committer.Name,
41-
Email: c.Committer.Email,
42-
}
43-
}
44-
4538
signCommit := &asymkey_model.SignCommit{
4639
UserCommit: c,
4740
Verification: asymkey_service.ParseCommitWithSignatureCommitter(ctx, c.Commit, committerUser),

templates/repo/commit_page.tmpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@
147147
<div class="flex-text-inline">
148148
{{if or (ne .Commit.Committer.Name .Commit.Author.Name) (ne .Commit.Committer.Email .Commit.Author.Email)}}
149149
<span class="text grey">{{ctx.Locale.Tr "repo.diff.committed_by"}}</span>
150-
{{if ne .Verification.CommittingUser.ID 0}}
150+
{{if and .Verification.CommittingUser .Verification.CommittingUser.ID}}
151151
{{ctx.AvatarUtils.Avatar .Verification.CommittingUser 20}}
152152
<a href="{{.Verification.CommittingUser.HomeLink}}"><strong>{{.Commit.Committer.Name}}</strong></a>
153153
{{else}}

0 commit comments

Comments
 (0)