Skip to content

Commit

Permalink
feat(policy, witness): added decisionLogUrl argument to verify
Browse files Browse the repository at this point in the history
  • Loading branch information
kriscoleman committed Aug 4, 2023
1 parent 6e5c872 commit 4bd19be
Show file tree
Hide file tree
Showing 21 changed files with 1,338 additions and 217 deletions.
130 changes: 14 additions & 116 deletions attestation/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,81 +18,15 @@ import (
"fmt"

"github.com/testifysec/go-witness/cryptoutil"
"github.com/testifysec/go-witness/registry"
)

var (
attestationsByName = map[string]AttestorRegistration{}
attestationsByType = map[string]AttestorRegistration{}
attestationsByRun = map[RunType]AttestorRegistration{}
attestorRegistry = registry.New[Attestor]()
attestationsByType = map[string]registry.Entry[Attestor]{}
attestationsByRun = map[RunType]registry.Entry[Attestor]{}
)

type Configurer interface {
Description() string
Name() string
}

type Configurable interface {
int | string | []string
}

type ConfigOption[T Configurable] struct {
name string
description string
defaultVal T
setter func(Attestor, T) (Attestor, error)
}

func (co ConfigOption[T]) Name() string {
return co.name
}

func (co ConfigOption[T]) DefaultVal() T {
return co.defaultVal
}

func (co ConfigOption[T]) Description() string {
return co.description
}

func (co ConfigOption[T]) Setter() func(Attestor, T) (Attestor, error) {
return co.setter
}

func IntConfigOption(name, description string, defaultVal int, setter func(Attestor, int) (Attestor, error)) ConfigOption[int] {
return ConfigOption[int]{
name,
description,
defaultVal,
setter,
}
}

func StringConfigOption(name, description string, defaultVal string, setter func(Attestor, string) (Attestor, error)) ConfigOption[string] {
return ConfigOption[string]{
name,
description,
defaultVal,
setter,
}
}

func StringSliceConfigOption(name, description string, defaultVal []string, setter func(Attestor, []string) (Attestor, error)) ConfigOption[[]string] {
return ConfigOption[[]string]{
name,
description,
defaultVal,
setter,
}
}

type AttestorRegistration struct {
Factory AttestorFactory
Name string
Type string
RunType RunType
Options []Configurer
}

type Attestor interface {
Name() string
Type() string
Expand Down Expand Up @@ -130,35 +64,25 @@ type BackReffer interface {
BackRefs() map[string]cryptoutil.DigestSet
}

type AttestorFactory func() Attestor

type ErrAttestationNotFound string

func (e ErrAttestationNotFound) Error() string {
return fmt.Sprintf("attestation not found: %v", string(e))
}

func RegisterAttestation(name, predicateType string, run RunType, factoryFunc AttestorFactory, opts ...Configurer) {
registrationEntry := AttestorRegistration{
Name: name,
Type: predicateType,
Factory: factoryFunc,
RunType: run,
Options: opts,
}

attestationsByName[name] = registrationEntry
func RegisterAttestation(name, predicateType string, run RunType, factoryFunc registry.FactoryFunc[Attestor], opts ...registry.Configurer) {
registrationEntry := attestorRegistry.Register(name, factoryFunc, opts...)
attestationsByType[predicateType] = registrationEntry
attestationsByRun[run] = registrationEntry
}

func FactoryByType(uri string) (AttestorFactory, bool) {
func FactoryByType(uri string) (registry.FactoryFunc[Attestor], bool) {
registrationEntry, ok := attestationsByType[uri]
return registrationEntry.Factory, ok
}

func FactoryByName(name string) (AttestorFactory, bool) {
registrationEntry, ok := attestationsByName[name]
func FactoryByName(name string) (registry.FactoryFunc[Attestor], bool) {
registrationEntry, ok := attestorRegistry.Entry(name)
return registrationEntry.Factory, ok
}

Expand All @@ -175,7 +99,7 @@ func Attestors(nameOrTypes []string) ([]Attestor, error) {

attestor := factory()
opts := AttestorOptions(nameOrType)
attestor, err := setDefaultVals(attestor, opts)
attestor, err := attestorRegistry.SetDefaultVals(attestor, opts)
if err != nil {
return nil, err
}
Expand All @@ -186,41 +110,15 @@ func Attestors(nameOrTypes []string) ([]Attestor, error) {
return attestors, nil
}

func AttestorOptions(nameOrType string) []Configurer {
entry, ok := attestationsByName[nameOrType]
func AttestorOptions(nameOrType string) []registry.Configurer {
entry, ok := attestorRegistry.Entry(nameOrType)
if !ok {
entry = attestationsByType[nameOrType]
}

return entry.Options
}

func RegistrationEntries() []AttestorRegistration {
results := make([]AttestorRegistration, 0, len(attestationsByName))
for _, registration := range attestationsByName {
results = append(results, registration)
}

return results
}

func setDefaultVals(attestor Attestor, opts []Configurer) (Attestor, error) {
var err error

for _, opt := range opts {
switch o := opt.(type) {
case ConfigOption[int]:
attestor, err = o.Setter()(attestor, o.DefaultVal())
case ConfigOption[string]:
attestor, err = o.Setter()(attestor, o.DefaultVal())
case ConfigOption[[]string]:
attestor, err = o.Setter()(attestor, o.DefaultVal())
}

if err != nil {
return attestor, err
}
}

return attestor, nil
func RegistrationEntries() []registry.Entry[Attestor] {
return attestorRegistry.AllEntries()
}
60 changes: 0 additions & 60 deletions attestation/factory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
package attestation

import (
"errors"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -53,65 +52,6 @@ func TestRegistry(t *testing.T) {
}
}

func TestConfigOptions(t *testing.T) {
defaultIntVal := 50
defaultStrVal := "default string"
defaultStrSliceVal := []string{"d", "e", "f"}
attestorOpts := []Configurer{
IntConfigOption("someint", "some int", defaultIntVal, func(a Attestor, v int) (Attestor, error) {
da := a.(*dummyAttestor)
da.intOpt = v
return da, nil
}),
StringConfigOption("somestring", "some string", defaultStrVal, func(a Attestor, v string) (Attestor, error) {
da := a.(*dummyAttestor)
da.strOpt = v
return da, nil
}),
StringSliceConfigOption("someslice", "some slice", defaultStrSliceVal, func(a Attestor, v []string) (Attestor, error) {
da := a.(*dummyAttestor)
da.strSliceOpt = v
return da, nil
}),
}

RegisterAttestation("optionTest", "optionTest", PreMaterialRunType, func() Attestor { return &dummyAttestor{} }, attestorOpts...)
opts := AttestorOptions("optionTest")
attestors, err := Attestors([]string{"optionTest"})
require.NoError(t, err)
require.Len(t, attestors, 1)
optAttestor := attestors[0]
assert.Equal(t, optAttestor, &dummyAttestor{
intOpt: defaultIntVal,
strOpt: defaultStrVal,
strSliceOpt: defaultStrSliceVal,
})

intVal := 100
strVal := "test string"
strSliceVal := []string{"a", "b", "c"}
for _, opt := range opts {
switch o := opt.(type) {
case ConfigOption[int]:
_, err = o.Setter()(optAttestor, intVal)
case ConfigOption[string]:
_, err = o.Setter()(optAttestor, strVal)
case ConfigOption[[]string]:
_, err = o.Setter()(optAttestor, strSliceVal)
default:
err = errors.New("unknown config option")
}

require.NoError(t, err)
}

assert.Equal(t, optAttestor, &dummyAttestor{
intOpt: intVal,
strOpt: strVal,
strSliceOpt: strSliceVal,
})
}

type dummyAttestor struct {
name string
predicateType string
Expand Down
4 changes: 4 additions & 0 deletions attestation/git/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package git
import (
"crypto"
"fmt"
"strings"
"time"

"github.com/go-git/go-git/v5"
Expand Down Expand Up @@ -106,6 +107,9 @@ func (a *Attestor) Attest(ctx *attestation.AttestationContext) error {

head, err := repo.Head()
if err != nil {
if strings.Contains(err.Error(), "reference not found") {
return nil
}
return err
}

Expand Down
46 changes: 34 additions & 12 deletions attestation/git/git_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ func TestNameTypeRunType(t *testing.T) {
require.Equal(t, RunType, attestor.RunType(), "Expected the attestor's run type")
}

func TestRun(t *testing.T) {
func TestRunWorksWithCommits(t *testing.T) {
attestor := New()

_, dir, cleanup := createTestRepo(t)
_, dir, cleanup := createTestRepo(t, true)
defer cleanup()

ctx, err := attestation.NewContext([]attestation.Attestor{attestor}, attestation.WithWorkingDir(dir))
Expand Down Expand Up @@ -140,7 +140,23 @@ func TestRun(t *testing.T) {

}

func createTestRepo(t *testing.T) (*git.Repository, string, func()) {
func TestRunWorksWithoutCommits(t *testing.T) {
attestor := New()

_, dir, cleanup := createTestRepo(t, false)
defer cleanup()

ctx, err := attestation.NewContext([]attestation.Attestor{attestor}, attestation.WithWorkingDir(dir))
require.NoError(t, err, "Expected no error from NewContext")

err = ctx.RunAttestors()
require.NoError(t, err, "Expected no error from RunAttestors")

require.Empty(t, attestor.ParentHashes, "Expected the parent hashes to be set")
}

// Creates an ephemeral repo for your testing
func createTestRepo(t *testing.T, withCommit bool) (*git.Repository, string, func()) {
// Create a temporary directory for the test repository
tmpDir, err := os.MkdirTemp("", "test-repo")
require.NoError(t, err)
Expand All @@ -149,7 +165,21 @@ func createTestRepo(t *testing.T) (*git.Repository, string, func()) {
repo, err := git.PlainInit(tmpDir, false)
require.NoError(t, err)

// Create a new file in the repository
if withCommit {
addCommit(tmpDir, t, repo)
}

// Return the test repository, the path to the test repository, and a cleanup function
return repo, tmpDir, func() {
err := os.RemoveAll(tmpDir)
require.NoError(t, err)
}
}

// Create a new file in the repository
// Add the new file to the repository
// Commit the new file to the repository
func addCommit(tmpDir string, t *testing.T, repo *git.Repository) {
filePath := filepath.Join(tmpDir, "test.txt")
file, err := os.Create(filePath)
require.NoError(t, err)
Expand All @@ -158,13 +188,11 @@ func createTestRepo(t *testing.T) (*git.Repository, string, func()) {
err = file.Close()
require.NoError(t, err)

// Add the new file to the repository
worktree, err := repo.Worktree()
require.NoError(t, err)
_, err = worktree.Add("test.txt")
require.NoError(t, err)

// Commit the new file to the repository
_, err = worktree.Commit("Initial commit", &git.CommitOptions{
Author: &object.Signature{
Name: "Test User",
Expand All @@ -173,12 +201,6 @@ func createTestRepo(t *testing.T) (*git.Repository, string, func()) {
},
})
require.NoError(t, err)

// Return the test repository, the path to the test repository, and a cleanup function
return repo, tmpDir, func() {
err := os.RemoveAll(tmpDir)
require.NoError(t, err)
}
}
func createTestCommit(t *testing.T, repoPath string, message string) {
// Open the Git repository
Expand Down
9 changes: 5 additions & 4 deletions attestation/product/product.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/testifysec/go-witness/attestation"
"github.com/testifysec/go-witness/attestation/file"
"github.com/testifysec/go-witness/cryptoutil"
"github.com/testifysec/go-witness/registry"
)

const (
Expand All @@ -48,8 +49,8 @@ var (

func init() {
attestation.RegisterAttestation(Name, Type, RunType, func() attestation.Attestor { return New() },
attestation.StringConfigOption(
"includeGlob",
registry.StringConfigOption(
"include-glob",
"Pattern to use when recording products. Files that match this pattern will be included as subjects on the attestation.",
defaultIncludeGlob,
func(a attestation.Attestor, includeGlob string) (attestation.Attestor, error) {
Expand All @@ -62,8 +63,8 @@ func init() {
return prodAttestor, nil
},
),
attestation.StringConfigOption(
"excludeGlob",
registry.StringConfigOption(
"exclude-glob",
"Pattern to use when recording products. Files that match this pattern will be excluded as subjects on the attestation.",
defaultExcludeGlob,
func(a attestation.Attestor, excludeGlob string) (attestation.Attestor, error) {
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
github.com/digitorus/timestamp v0.0.0-20230220124323-d542479a2425
github.com/edwarnicke/gitoid v0.0.0-20220710194850-1be5bfda1f9d
github.com/go-git/go-git/v5 v5.5.2
github.com/google/uuid v1.3.0
github.com/mattn/go-isatty v0.0.17
github.com/open-policy-agent/opa v0.49.1
github.com/owenrumney/go-sarif v1.1.1
Expand Down
Loading

0 comments on commit 4bd19be

Please sign in to comment.