Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support checking GPG signature for ranges #262

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 38 additions & 25 deletions internal/git/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ type Git struct {
repo *git.Repository
}

type CommitData struct {
Message string
SHA string
}

func findDotGit(name string) (string, error) {
if _, err := os.Stat(name); os.IsNotExist(err) {
return findDotGit(path.Join("..", name))
Expand All @@ -49,19 +54,19 @@ func NewGit() (*Git, error) {
return &Git{repo: repo}, nil
}

// Message returns the commit message. In the case that a commit has multiple
// Commit returns the commit data. In the case that a commit has multiple
// parents, the message of the last parent is returned.
//
//nolint:nonamedreturns
func (g *Git) Message() (message string, err error) {
func (g *Git) Commit() (commitData CommitData, err error) {
ref, err := g.repo.Head()
if err != nil {
return "", err
return CommitData{Message: ""}, err
}

commit, err := g.repo.CommitObject(ref.Hash())
if err != nil {
return "", err
return CommitData{Message: "", SHA: ref.Hash().String()}, err
}

if commit.NumParents() > 1 {
Expand All @@ -72,22 +77,22 @@ func (g *Git) Message() (message string, err error) {

next, err = parents.Next()
if err != nil {
return "", err
return CommitData{Message: ""}, err
}

if i == commit.NumParents() {
message = next.Message
commitData = CommitData{Message: next.Message, SHA: next.Hash.String()}
}
}
} else {
message = commit.Message
commitData = CommitData{Message: commit.Message, SHA: commit.Hash.String()}
}

return message, err
return commitData, err
}

// Messages returns the list of commit messages in the range commit1..commit2.
func (g *Git) Messages(commit1, commit2 string) ([]string, error) {
// Commits returns the list of commit data in the range commit1..commit2.
func (g *Git) Commits(commit1, commit2 string) ([]CommitData, error) {
hash1, err := g.repo.ResolveRevision(plumbing.Revision(commit1))
if err != nil {
return nil, err
Expand Down Expand Up @@ -117,10 +122,11 @@ func (g *Git) Messages(commit1, commit2 string) ([]string, error) {
c1 = c[0]
}

msgs := make([]string, 0)
commits := make([]CommitData, 0)

for {
msgs = append(msgs, c2.Message)
commit := CommitData{Message: c2.Message, SHA: c2.Hash.String()}
commits = append(commits, commit)

c2, err = c2.Parents().Next()
if err != nil {
Expand All @@ -132,20 +138,24 @@ func (g *Git) Messages(commit1, commit2 string) ([]string, error) {
}
}

return msgs, nil
return commits, nil
}

// HasGPGSignature returns the commit message. In the case that a commit has multiple
// parents, the message of the last parent is returned.
// HasGPGSignature verifies that the given commit has a GPG signature.
// In the case that sha is empty. The last commit is checked.
//
//nolint:nonamedreturns
func (g *Git) HasGPGSignature() (ok bool, err error) {
ref, err := g.repo.Head()
if err != nil {
return false, err
func (g *Git) HasGPGSignature(sha string) (ok bool, err error) {
if sha == "" {
ref, err := g.repo.Head()
if err != nil {
return false, err
}
sha = ref.Hash().String()
}

commit, err := g.repo.CommitObject(ref.Hash())

commit, err := g.repo.CommitObject(plumbing.NewHash(sha))
if err != nil {
return false, err
}
Expand All @@ -156,13 +166,16 @@ func (g *Git) HasGPGSignature() (ok bool, err error) {
}

// VerifyPGPSignature validates PGP signature against a keyring.
func (g *Git) VerifyPGPSignature(armoredKeyrings []string) (*openpgp.Entity, error) {
ref, err := g.repo.Head()
if err != nil {
return nil, err
func (g *Git) VerifyPGPSignature(sha string, armoredKeyrings []string) (*openpgp.Entity, error) {
if sha == "" {
ref, err := g.repo.Head()
if err != nil {
return nil, err
}
sha = ref.Hash().String()
}

commit, err := g.repo.CommitObject(ref.Hash())
commit, err := g.repo.CommitObject(plumbing.NewHash(sha))
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion internal/policy/commit/check_gpg_identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func (c Commit) ValidateGPGIdentity(g *git.Git) policy.Check { //nolint:ireturn
return check
}

entity, err := g.VerifyPGPSignature(keyrings)
entity, err := g.VerifyPGPSignature(c.sha, keyrings)
if err != nil {
check.errors = append(check.errors, err)

Expand Down
2 changes: 1 addition & 1 deletion internal/policy/commit/check_gpg_signature.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func (g GPGCheck) Errors() []error {
func (c Commit) ValidateGPGSign(g *git.Git) policy.Check { //nolint:ireturn
check := &GPGCheck{}

ok, err := g.HasGPGSignature()
ok, err := g.HasGPGSignature(c.sha)
if err != nil {
check.errors = append(check.errors, err)

Expand Down
16 changes: 9 additions & 7 deletions internal/policy/commit/commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ type Commit struct {
MaximumOfOneCommit bool `mapstructure:"maximumOfOneCommit"`

msg string
sha string
}

// FirstWordRegex is theregular expression used to find the first word in a
Expand All @@ -102,7 +103,7 @@ func (c *Commit) Compliance(options *policy.Options) (*policy.Report, error) {
return report, errors.Errorf("failed to open git repo: %v", err)
}

var msgs []string
var commits []git.CommitData

switch o := options; {
case o.CommitMsgFile != nil:
Expand All @@ -112,28 +113,29 @@ func (c *Commit) Compliance(options *policy.Options) (*policy.Report, error) {
return report, errors.Errorf("failed to read commit message file: %v", err)
}

msgs = append(msgs, string(contents))
commits = append(commits, git.CommitData{Message: string(contents)})
case o.RevisionRange != "":
revs, err := extractRevisionRange(options)
if err != nil {
return report, errors.Errorf("failed to get commit message: %v", err)
}

msgs, err = g.Messages(revs[0], revs[1])
commits, err = g.Commits(revs[0], revs[1])
if err != nil {
return report, errors.Errorf("failed to get commit message: %v", err)
}
default:
msg, err := g.Message()
commit, err := g.Commit()
if err != nil {
return report, errors.Errorf("failed to get commit message: %v", err)
}

msgs = append(msgs, msg)
commits = append(commits, commit)
}

for i := range msgs {
c.msg = msgs[i]
for i := range commits {
c.msg = commits[i].Message
c.sha = commits[i].SHA

c.compliance(report, g, options)
}
Expand Down