• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

gittuf / gittuf / 27768702340

18 Jun 2026 03:01PM UTC coverage: 75.042%. Remained the same
27768702340

push

github

web-flow
Merge pull request #1400 from gittuf/bump-go-git

Bump go-git to v6

4 of 6 new or added lines in 2 files covered. (66.67%)

9351 of 12461 relevant lines covered (75.04%)

43.69 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

63.25
/pkg/gitinterface/tag.go
1
// Copyright The gittuf Authors
2
// SPDX-License-Identifier: Apache-2.0
3

4
package gitinterface
5

6
import (
7
        "context"
8
        "errors"
9
        "fmt"
10
        "io"
11
        "strings"
12

13
        "github.com/gittuf/gittuf/internal/signerverifier/gpg"
14
        "github.com/gittuf/gittuf/internal/signerverifier/sigstore"
15
        "github.com/gittuf/gittuf/internal/signerverifier/ssh"
16
        "github.com/go-git/go-git/v6/plumbing"
17
        "github.com/go-git/go-git/v6/plumbing/object"
18
        "github.com/go-git/go-git/v6/storage/memory"
19
        "github.com/secure-systems-lab/go-securesystemslib/signerverifier"
20
)
21

22
var (
23
        ErrTagAlreadyExists = errors.New("tag already exists")
24
)
25

26
// TagUsingSpecificKey creates a Git tag signed using the specified, PEM encoded
27
// SSH or GPG key. It is primarily intended for use with testing. As of now,
28
// gittuf is not expected to be used to create tags in developer workflows,
29
// though this may change with command compatibility.
30
func (r *Repository) TagUsingSpecificKey(target Hash, name, message string, signingKeyPEMBytes []byte) (Hash, error) {
7✔
31
        gitConfig, err := r.GetGitConfig()
7✔
32
        if err != nil {
7✔
33
                return ZeroHash, err
×
34
        }
×
35

36
        goGitRepo, err := r.GetGoGitRepository()
7✔
37
        if err != nil {
7✔
38
                return ZeroHash, err
×
39
        }
×
40

41
        targetObj, err := goGitRepo.Object(plumbing.AnyObject, plumbing.NewHash(target.String()))
7✔
42
        if err != nil {
8✔
43
                return ZeroHash, err
1✔
44
        }
1✔
45

46
        if !strings.HasSuffix(message, "\n") {
6✔
47
                message += "\n"
×
48
        }
×
49

50
        tag := &object.Tag{
6✔
51
                Name: name,
6✔
52
                Tagger: object.Signature{
6✔
53
                        Name:  gitConfig["user.name"],
6✔
54
                        Email: gitConfig["user.email"],
6✔
55
                        When:  r.clock.Now(),
6✔
56
                },
6✔
57
                Message:    message,
6✔
58
                TargetType: targetObj.Type(),
6✔
59
                Target:     targetObj.ID(),
6✔
60
        }
6✔
61

6✔
62
        tagContents, err := getTagBytesWithoutSignature(tag)
6✔
63
        if err != nil {
6✔
64
                return ZeroHash, err
×
65
        }
×
66
        signature, err := signGitObjectUsingKey(tagContents, signingKeyPEMBytes)
6✔
67
        if err != nil {
6✔
68
                return ZeroHash, err
×
69
        }
×
70
        tag.Signature = signature
6✔
71

6✔
72
        obj := goGitRepo.Storer.NewEncodedObject()
6✔
73
        if err := tag.Encode(obj); err != nil {
6✔
74
                return ZeroHash, err
×
75
        }
×
76
        tagID, err := goGitRepo.Storer.SetEncodedObject(obj)
6✔
77
        if err != nil {
6✔
78
                return ZeroHash, err
×
79
        }
×
80

81
        tagIDHash, err := NewHash(tagID.String())
6✔
82
        if err != nil {
6✔
83
                return ZeroHash, err
×
84
        }
×
85

86
        return tagIDHash, r.SetReference(TagReferenceName(name), tagIDHash)
6✔
87
}
88

89
// GetTagTarget returns the ID of the Git object a tag points to.
90
func (r *Repository) GetTagTarget(tagID Hash) (Hash, error) {
2✔
91
        targetID, err := r.executor("rev-list", "-n", "1", tagID.String()).executeString()
2✔
92
        if err != nil {
3✔
93
                return ZeroHash, fmt.Errorf("unable to resolve tag's target ID: %w", err)
1✔
94
        }
1✔
95

96
        hash, err := NewHash(targetID)
1✔
97
        if err != nil {
1✔
98
                return ZeroHash, fmt.Errorf("invalid format for target ID: %w", err)
×
99
        }
×
100

101
        return hash, nil
1✔
102
}
103

104
// verifyTagSignature verifies a signature for the specified tag using the
105
// provided public key.
106
func (r *Repository) verifyTagSignature(ctx context.Context, tagID Hash, key *signerverifier.SSLibKey) error {
3✔
107
        goGitRepo, err := r.GetGoGitRepository()
3✔
108
        if err != nil {
3✔
109
                return fmt.Errorf("error opening repository: %w", err)
×
110
        }
×
111

112
        tag, err := goGitRepo.TagObject(plumbing.NewHash(tagID.String()))
3✔
113
        if err != nil {
3✔
114
                return fmt.Errorf("unable to load commit object: %w", err)
×
115
        }
×
116

117
        switch key.KeyType {
3✔
118
        case gpg.KeyType:
1✔
119
                if _, err := tag.Verify(key.KeyVal.Public); err != nil {
1✔
120
                        return ErrIncorrectVerificationKey
×
121
                }
×
122

123
                return nil
1✔
124
        case ssh.KeyType:
1✔
125
                tagContents, err := getTagBytesWithoutSignature(tag)
1✔
126
                if err != nil {
1✔
127
                        return errors.Join(ErrVerifyingSSHSignature, err)
×
128
                }
×
129
                tagSignature := []byte(tag.Signature)
1✔
130

1✔
131
                if err := verifySSHKeySignature(ctx, key, tagContents, tagSignature); err != nil {
1✔
132
                        return errors.Join(ErrIncorrectVerificationKey, err)
×
133
                }
×
134

135
                return nil
1✔
136
        case sigstore.KeyType:
×
137
                tagContents, err := getTagBytesWithoutSignature(tag)
×
138
                if err != nil {
×
139
                        return errors.Join(ErrVerifyingSigstoreSignature, err)
×
140
                }
×
NEW
141
                tagSignature := []byte(tag.Signature)
×
142

×
143
                if err := verifyGitsignSignature(ctx, r, key, tagContents, tagSignature); err != nil {
×
144
                        return errors.Join(ErrIncorrectVerificationKey, err)
×
145
                }
×
146

147
                return nil
×
148
        }
149

150
        return ErrUnknownSigningMethod
1✔
151
}
152

153
func (r *Repository) ensureIsTag(tagID Hash) error {
4✔
154
        objType, err := r.executor("cat-file", "-t", tagID.String()).executeString()
4✔
155
        if err != nil {
5✔
156
                return fmt.Errorf("unable to inspect if object is tag: %w", err)
1✔
157
        } else if objType != "tag" {
6✔
158
                return fmt.Errorf("requested Git ID '%s' is not a tag object", tagID.String())
2✔
159
        }
2✔
160

161
        return nil
1✔
162
}
163

164
func getTagBytesWithoutSignature(tag *object.Tag) ([]byte, error) {
7✔
165
        tagEncoded := memory.NewStorage().NewEncodedObject()
7✔
166
        if err := tag.EncodeWithoutSignature(tagEncoded); err != nil {
7✔
167
                return nil, err
×
168
        }
×
169
        r, err := tagEncoded.Reader()
7✔
170
        if err != nil {
7✔
171
                return nil, err
×
172
        }
×
173

174
        return io.ReadAll(r)
7✔
175
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc