Skip to content

Commit

Permalink
added code for custom attestors poc
Browse files Browse the repository at this point in the history
Signed-off-by: chaosinthecrd <[email protected]>
  • Loading branch information
ChaosInTheCRD committed Jan 31, 2024
1 parent 77a9f42 commit f1c8b94
Show file tree
Hide file tree
Showing 6 changed files with 1,611 additions and 5 deletions.
4 changes: 2 additions & 2 deletions attestation/collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ func NewCollection(name string, attestors []CompletedAttestor) Collection {
Attestations: make([]CollectionAttestation, 0),
}

//move start/stop time to collection
//todo: this is a bit of a hack, but it's the easiest way to get the start/stop time
// move start/stop time to collection
// todo: this is a bit of a hack, but it's the easiest way to get the start/stop time

for _, completed := range attestors {
collection.Attestations = append(collection.Attestations, NewCollectionAttestation(completed))
Expand Down
18 changes: 18 additions & 0 deletions attestation/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,24 @@ func (r RunType) String() string {
return string(r)
}

func ParseRunType(r string) RunType {
switch r {
case "prematerial":
return PreMaterialRunType
case "material":
return MaterialRunType
case "execute":
return ExecuteRunType
case "product":
return ProductRunType
case "postproduct":
return PostProductRunType
default:
// NOTE: This is wrong and should be tidied up
return PreMaterialRunType
}
}

type ErrInvalidOption struct {
Option string
Reason string
Expand Down
140 changes: 140 additions & 0 deletions attestation/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,14 @@
package attestation

import (
"context"
"encoding/json"
"fmt"
"os/exec"

"github.com/in-toto/go-witness/cryptoutil"
"github.com/in-toto/go-witness/registry"
"github.com/qri-io/jsonschema"
)

var (
Expand All @@ -34,6 +38,134 @@ type Attestor interface {
Attest(ctx *AttestationContext) error
}

type CustomAttestor struct {
ValidatedType string
Definition CustomAttestorDefinition
Output []byte
}

func (c *CustomAttestor) MarshalJSON() ([]byte, error) {
var out OutputAttestation
err := json.Unmarshal(c.Output, &out)
if err != nil {
return nil, err
}
return json.Marshal(out.Attestation)
}

type CustomAttestorDefinition struct {
Version string `json:"version" yaml:"version"`
Metadata Metadata `json:"metadata" yaml:"metadata"`
Spec Spec `json:"spec" yaml:"spec"`
}

type Metadata struct {
Name string `json:"name" yaml:"name"`
Type string `json:"type" yaml:"type"`
}

type Spec struct {
RunType string `json:"runType" yaml:"runType"`
Executor Executor `json:"executor" yaml:"executor"`
Versions []Version `json:"versions" yaml:"versions"`
}

type Executor struct {
Attest Execute `json:"attest" yaml:"attest"`
Verify Execute `json:"verify" yaml:"verify"`
}

type Execute struct {
Type string `json:"type" yaml:"type"`
Arguments []string `json:"arguments" yaml:"arguments"`
}

type Version struct {
Name string `json:"name" yaml:"name"`
Schema jsonschema.Schema `json:"schema" yaml:"schema"`
}

type OutputAttestation struct {
Type string `json:"type"`
Attestation map[string]interface{} `json:"attestation"`
}

func (c *CustomAttestor) Name() string {
return c.Definition.Metadata.Name
}

func (c *CustomAttestor) RunType() RunType {
return ParseRunType(c.Definition.Spec.RunType)
}

func (c *CustomAttestor) Type() string {
return c.ValidatedType
}

func (c *CustomAttestor) Attest(ctx *AttestationContext) error {
attest := c.Definition.Spec.Executor.Attest
var output []byte
var err error
if attest.Type == "command" {
var command string
var args []string
if len(attest.Arguments) == 0 {
return fmt.Errorf("no command specified")
} else if len(attest.Arguments) == 1 {
command = attest.Arguments[0]
} else {
command = attest.Arguments[0]
args = attest.Arguments[1:]
}

cmd := exec.Command(command, args...)
output, err = cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("Error running custom attestor: %v", err)
}
} else {
return fmt.Errorf("Executor type not supported")
}

// verify the output against the json Schema
var schema *jsonschema.Schema
var att OutputAttestation
err = json.Unmarshal(output, &att)
if err != nil {
return fmt.Errorf("Error unmarshalling custom attestor output: %v", err)
}
for _, v := range c.Definition.Spec.Versions {
t := fmt.Sprintf("%s/%s/%s", c.Definition.Metadata.Type, c.Definition.Metadata.Name, v.Name)
if att.Type == t {
c.ValidatedType = t
schema = &v.Schema
break
}
}

if c.ValidatedType == "" {
return fmt.Errorf("no matching version found for attestation outputted by custom attestor")
}

out, err := json.Marshal(att.Attestation)
if err != nil {
return fmt.Errorf("Error marshalling custom attestor output: %v", err)
}

errs, err := schema.ValidateBytes(context.Background(), out)
if err != nil {
return fmt.Errorf("Error validating custom attestor output: %v", err)
}

if len(errs) > 0 {
return fmt.Errorf("outputted JSON does not match schema: %v", errs)
}

c.Output = output

return nil
}

// Subjecter allows attestors to expose bits of information that will be added to
// the in-toto statement as subjects. External services such as Rekor and Archivista
// use in-toto subjects as indexes back to attestations.
Expand Down Expand Up @@ -82,6 +214,14 @@ func RegisterAttestation(name, predicateType string, run RunType, factoryFunc re
attestationsByRun[run] = registrationEntry
}

func RegisterCustomAttestation(definition CustomAttestorDefinition) {
registrationEntry := attestorRegistry.Register(definition.Metadata.Name, func() Attestor {
return &CustomAttestor{Definition: definition}
})
attestationsByType[definition.Metadata.Type] = registrationEntry
attestationsByRun[ParseRunType(definition.Spec.RunType)] = registrationEntry
}

func FactoryByType(uri string) (registry.FactoryFunc[Attestor], bool) {
registrationEntry, ok := attestationsByType[uri]
return registrationEntry.Factory, ok
Expand Down
13 changes: 13 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -29,27 +29,40 @@ require (
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/getkin/kin-openapi v0.123.0 // indirect
github.com/go-jose/go-jose/v3 v3.0.1 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-openapi/jsonpointer v0.20.2 // indirect
github.com/go-openapi/swag v0.22.8 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/google/flatbuffers v2.0.8+incompatible // indirect
github.com/google/gnostic v0.7.0 // indirect
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect
github.com/google/go-containerregistry v0.13.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.0 // indirect
github.com/invopop/yaml v0.2.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/letsencrypt/boulder v0.0.0-20221109233200-85aa52084eaf // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/perimeterx/marshmallow v1.1.5 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/qri-io/jsonpointer v0.1.1 // indirect
github.com/qri-io/jsonschema v0.2.1 // indirect
github.com/segmentio/ksuid v1.0.4 // indirect
github.com/skeema/knownhosts v1.2.1 // indirect
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
github.com/zclconf/go-cty v1.12.1 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/oauth2 v0.13.0 // indirect
Expand Down
Loading

0 comments on commit f1c8b94

Please sign in to comment.