Skip to content

Commit

Permalink
feat: provide gpg identity signature check
Browse files Browse the repository at this point in the history
Breaking change: `.conform.yaml` `gpg: bool` field was changed to be a
structure:

```yaml
gpg:
  required: true
  identity:
    gitHubOrganization: talos-systems
```

(`identity` is not required)

This enforces that GPG signature should come from one the members of the
GitHub organization to succeed, i.e. commit is signed by the member of
the organization.

Signed-off-by: Andrey Smirnov <[email protected]>
  • Loading branch information
smira committed Sep 1, 2021
1 parent c23e2fc commit fb43bd4
Show file tree
Hide file tree
Showing 19 changed files with 325 additions and 151 deletions.
5 changes: 4 additions & 1 deletion .conform.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ policies:
body:
required: true
dco: true
gpg: false
gpg:
required: true
identity:
gitHubOrganization: talos-systems
spellcheck:
locale: US
maximumOfOneCommit: true
Expand Down
2 changes: 1 addition & 1 deletion .drone.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ name: default

services:
- name: docker
image: docker:19.03-dind
image: docker:20.10-dind
entrypoint: [dockerd]
privileged: true
volumes:
Expand Down
2 changes: 2 additions & 0 deletions hack/golangci-lint.yaml → .golangci-lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ linters:
- exhaustivestruct
- errorlint
- wrapcheck
- forbidigo
- paralleltest
disable-all: false
fast: false

Expand Down
6 changes: 3 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ RUN go build -o /conform-${GOOS}-${GOARCH} -ldflags "-s -w -X \"github.com/talos
FROM common AS test
ENV GOOS linux
ENV GOARCH amd64
COPY ./hack ./hack
RUN chmod +x ./hack/test.sh
RUN ./hack/test.sh --all
RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.42.0
RUN golangci-lint run
RUN CGO_ENABLED=1 go test -v -race -covermode=atomic -coverprofile=/coverage.txt ./...

FROM alpine:3.11 as ca-certificates
RUN apk add --update --no-cache ca-certificates
Expand Down
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
SHA ?= $(shell git describe --match=none --always --abbrev=8)
TAG ?= $(shell git describe --tag --always)

GOLANG_IMAGE ?= golang:1.14
GOLANG_IMAGE ?= golang:1.16

COMMON_ARGS := -f ./Dockerfile --build-arg GOLANG_IMAGE=$(GOLANG_IMAGE) --build-arg SHA=$(SHA) --build-arg TAG=$(TAG) .

Expand All @@ -23,6 +23,7 @@ build:

test:
@docker build \
--network=host \
-t conform/$@:$(TAG) \
--target=$@ \
$(COMMON_ARGS)
Expand Down
6 changes: 1 addition & 5 deletions cmd/enforce.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,7 @@ var enforceCmd = &cobra.Command{
opts = append(opts, policy.WithCommitRef(commitRef))
}

if err := e.Enforce(opts...); err != nil {
return err
}

return nil
return e.Enforce(opts...)
},
}

Expand Down
9 changes: 5 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,25 @@ require (
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect
github.com/deckarep/golang-set v1.7.1 // indirect
github.com/denormal/go-gitignore v0.0.0-20180930084346-ae8ad1d07817
github.com/go-git/go-git/v5 v5.0.0
github.com/go-git/go-git/v5 v5.4.2
github.com/golangci/misspell v0.3.4
github.com/google/go-cmp v0.3.1 // indirect
github.com/google/go-github v17.0.0+incompatible
github.com/google/go-querystring v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4
github.com/mingrammer/commonregex v1.0.1 // indirect
github.com/mitchellh/mapstructure v0.0.0-20170523030023-d0303fe80992
github.com/montanaflynn/stats v0.5.0 // indirect
github.com/neurosnap/sentences v1.0.6 // indirect
github.com/pkg/errors v0.8.1
github.com/pkg/errors v0.9.1
github.com/spf13/cobra v0.0.3
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/net v0.0.0-20201021035429-f5854403a974 // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
gonum.org/v1/gonum v0.6.1 // indirect
gopkg.in/jdkato/prose.v2 v2.0.0-20180825173540-767a23049b9e
gopkg.in/neurosnap/sentences.v1 v1.0.6 // indirect
gopkg.in/yaml.v2 v2.2.4
gopkg.in/yaml.v2 v2.3.0
)

go 1.13
102 changes: 58 additions & 44 deletions go.sum

Large diffs are not rendered by default.

53 changes: 0 additions & 53 deletions hack/test.sh

This file was deleted.

15 changes: 14 additions & 1 deletion internal/enforcer/enforcer.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ func (c *Conform) Enforce(setters ...policy.Option) error {

pass = false
} else {
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t\n", p.Type, check.Name(), "PASS", "<none>")
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t\n", p.Type, check.Name(), "PASS", check.Message())

if err := c.reporter.SetStatus("success", p.Type, check.Name(), check.Message()); err != nil {
log.Printf("WARNING: report failed: %+v", err)
Expand All @@ -125,6 +125,19 @@ func (c *Conform) enforce(declaration *PolicyDeclaration, opts *policy.Options)

p := policyMap[declaration.Type]

// backwards compatibility, convert `gpg: bool` into `gpg: required: bool`
if declaration.Type == "commit" {
if spec, ok := declaration.Spec.(map[interface{}]interface{}); ok {
if gpg, ok := spec["gpg"]; ok {
if val, ok := gpg.(bool); ok {
spec["gpg"] = map[string]interface{}{
"required": val,
}
}
}
}
}

err := mapstructure.Decode(declaration.Spec, p)
if err != nil {
return nil, errors.Errorf("Internal error: %v", err)
Expand Down
61 changes: 49 additions & 12 deletions internal/git/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ import (
"os"
"path"
"path/filepath"
"strings"

git "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/plumbing/storer"
"github.com/keybase/go-crypto/openpgp"
)

// Git is a helper for git.
Expand Down Expand Up @@ -101,6 +103,49 @@ func (g *Git) HasGPGSignature() (ok bool, err error) {
return ok, err
}

// 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
}

commit, err := g.repo.CommitObject(ref.Hash())
if err != nil {
return nil, err
}

var keyring openpgp.EntityList

for _, armoredKeyring := range armoredKeyrings {
var el openpgp.EntityList

el, err = openpgp.ReadArmoredKeyRing(strings.NewReader(armoredKeyring))
if err != nil {
return nil, err
}

keyring = append(keyring, el...)
}

// Extract signature.
signature := strings.NewReader(commit.PGPSignature)

encoded := &plumbing.MemoryObject{}

// Encode commit components, excluding signature and get a reader object.
if err = commit.EncodeWithoutSignature(encoded); err != nil {
return nil, err
}

er, err := encoded.Reader()
if err != nil {
return nil, err
}

return openpgp.CheckArmoredDetachedSignature(keyring, er, signature)
}

// FetchPullRequest fetches a remote PR.
func (g *Git) FetchPullRequest(remote string, number int) (err error) {
opts := &git.FetchOptions{
Expand All @@ -110,11 +155,7 @@ func (g *Git) FetchPullRequest(remote string, number int) (err error) {
},
}

if err = g.repo.Fetch(opts); err != nil {
return err
}

return nil
return g.repo.Fetch(opts)
}

// CheckoutPullRequest checks out pull request.
Expand All @@ -128,11 +169,7 @@ func (g *Git) CheckoutPullRequest(number int) (err error) {
Branch: plumbing.ReferenceName(fmt.Sprintf("pr/%d", number)),
}

if err := w.Checkout(opts); err != nil {
return err
}

return nil
return w.Checkout(opts)
}

// SHA returns the sha of the current commit.
Expand Down Expand Up @@ -162,7 +199,7 @@ func (g *Git) AheadBehind(ref string) (ahead int, behind int, err error) {

commit2, err := object.GetCommit(g.repo.Storer, ref2.Hash())
if err != nil {
return 0, 0, nil
return 0, 0, nil //nolint:nilerr
}

var count int
Expand All @@ -180,7 +217,7 @@ func (g *Git) AheadBehind(ref string) (ahead int, behind int, err error) {
})

if err != nil {
return 0, 0, nil
return 0, 0, nil //nolint:nilerr
}

return count, 0, nil
Expand Down
2 changes: 1 addition & 1 deletion internal/policy/commit/check_conventional_commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func (c ConventionalCommitCheck) Errors() []error {
}

// ValidateConventionalCommit returns the commit type.
// nolint: gocyclo
//nolint:gocyclo,cyclop
func (c Commit) ValidateConventionalCommit() policy.Check {
check := &ConventionalCommitCheck{}
groups := parseHeader(c.msg)
Expand Down
Loading

0 comments on commit fb43bd4

Please sign in to comment.