Skip to content

Commit

Permalink
Merge pull request #220 from marcelamelara/add-go-apis
Browse files Browse the repository at this point in the history
Add Golang wrapper APIs and tests
  • Loading branch information
pxp928 authored May 25, 2023
2 parents 906edfd + 44a8df6 commit c7dc920
Show file tree
Hide file tree
Showing 12 changed files with 335 additions and 38 deletions.
29 changes: 29 additions & 0 deletions .github/workflows/run-go-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Run Go tests
on:
workflow_dispatch:

# Want to trigger these tests whenever the attestation
# libraries are regenerated, or new modules/tests are
# added to the language bindings
pull_request:
paths:
- 'go/**'
permissions: read-all
jobs:
tests:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
- name: Install Go
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9
with:
go-version: 1.20.x
- name: Format
run: if [ "$(gofmt -s -l . | wc -l)" -gt 0 ]; then exit 1; fi
- name: Setup Env
run: |
echo "$(go env GOPATH)/bin" >> $GITHUB_PATH
- name: Run tests
run: |
make go_test
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@ protos: go_setup
go_run:
go run examples/go/main.go

.PHONY: protos go_setup go_run
go_test: go_setup
go test ./...

.PHONY: protos go_setup go_run go_test
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ including, but not limited to, the following high-level examples:
- [validation model](validation.md)
- [ideas for future schemas](schema_ideas.md)
- [protobuf definitions](protos.md)
- [testing the implementations](testing.md)

[Binary Authorization]: https://cloud.google.com/binary-authorization
[SLSA Provenance]: https://slsa.dev/provenance
Expand Down
26 changes: 26 additions & 0 deletions docs/testing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# in-toto attestation implementation tests

We provide a set of basic tests for the different language
bindings for the in-toto attestation layers.

## Testing the Go bindings

The go packages `go/v1` and `go/predicates` provide a number of tests
for the statement and predicate layers.

### Running the Go tests

To run all tests:

```shell
make go_test
```

### Writing new Go tests

Please use the standard [Golang testing package] to write tests
for new predicates. For example tests, take a look at the `*_test.go`
files in the `go/` directory tree.

At a minimum, we suggest testing JSON marshalling and unmarshalling
of the Go language bindings.
39 changes: 3 additions & 36 deletions examples/go/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,16 @@ func createStatementPbFromJson(subName string, subSha256 string, predicateType s
}

func createStatementPb(subName string, subSha256 string, predicateType string, predicate *structpb.Struct) *spb.Statement {
sub := []*spb.Statement_Subject{{
sub := []*spb.ResourceDescriptor{{
Name: subName,
Digest: map[string]string{"sha256": strings.ToLower(subSha256)},
}}
statement := &spb.Statement{
Type: "https://in-toto.io/Statement/v1",
return &spb.Statement{
Type: spb.StatementTypeUri,
Subject: sub,
PredicateType: predicateType,
Predicate: predicate,
}
return statement
}

func createVsa(subName string, subSha256 string, vsaBody *vpb.VerificationSummary) (*spb.Statement, error) {
Expand All @@ -48,31 +47,6 @@ func createVsa(subName string, subSha256 string, vsaBody *vpb.VerificationSummar
return createStatementPb(subName, subSha256, "https://slsa.dev/verification_summary/v0.2", vsaStruct), nil
}

func createTestResourceDescriptor() (*spb.ResourceDescriptor, error) {
// Create a ResourceDescriptor
a1, err := structpb.NewStruct(map[string]interface{}{
"keyStr": "value1",
"keyNum": 13})
if err != nil {
return nil, err
}
a2, err := structpb.NewStruct(map[string]interface{}{
"keyObj": map[string]interface{}{
"subKey": "subVal"}})
if err != nil {
return nil, err
}
r := &spb.ResourceDescriptor{
Name: "theName",
Uri: "http://example.com",
Digest: map[string]string{"sha256": "abc123"},
Content: []byte("bytescontent"),
DownloadLocation: "http://example.com/test.zip",
MediaType: "theMediaType",
Annotations: map[string]*structpb.Struct{"a1": a1, "a2": a2}}
return r, nil
}

// Example of how to use protobuf to create in-toto statements.
// Users will still likely want to put the json output in a DSSE.
func main() {
Expand Down Expand Up @@ -131,11 +105,4 @@ func main() {
}
fmt.Printf("\nRead statement with predicateType %v\n", s.PredicateType)
fmt.Printf("Predicate %v\n", s.Predicate)

// Test ResourceDescriptor
r, err := createTestResourceDescriptor()
if err != nil {
log.Fatal(err)
}
fmt.Printf("\nResourceDescriptor as json:\n%v\n", protojson.Format(r))
}
11 changes: 10 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,13 @@ module github.com/in-toto/attestation

go 1.20

require google.golang.org/protobuf v1.30.0
require (
github.com/stretchr/testify v1.8.2
google.golang.org/protobuf v1.30.0
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
17 changes: 17 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
6 changes: 6 additions & 0 deletions go/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ The Go bindings for the attestations layers and predicates are provided in
the `github.com/in-toto/attestation/go/v1` and
`github.com/in-toto/attestation/go/predicates` packages, respectively.

## Testing

See the [testing docs] for info and instructions for testing this implementation.

## Run the Go example

examples/go/main.go provides an example of how these protos can be used.
Expand All @@ -27,3 +31,5 @@ $ make go_run
Read statement with predicateType https://example.com/unknownPred2
Predicate fields:{key:"foo" value:{struct_value:{fields:{key:"bar" value:{string_value:"baz"}}}}}
```

[testing docs]: ../docs/testing.md#testing-the-go-bindings
16 changes: 16 additions & 0 deletions go/v1/resource_descriptor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
Wrapper APIs for in-toto attestation ResourceDescriptor protos.
*/

package v1

import "errors"

func (d *ResourceDescriptor) Validate() error {
// at least one of name, URI or digest are required
if d.GetName() == "" && d.GetUri() == "" && len(d.GetDigest()) == 0 {
return errors.New("At least one of name, URI or digest are required")
}

return nil
}
69 changes: 69 additions & 0 deletions go/v1/resource_descriptor_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
Tests for in-toto attestation ResourceDescriptor protos.
*/

package v1

import (
"testing"

"github.com/stretchr/testify/assert"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/structpb"
)

const wantFullRd = `{"name":"theName","uri":"https://example.com","digest":{"alg1":"abc123"},"content":"Ynl0ZXNjb250ZW50","downloadLocation":"https://example.com/test.zip","mediaType":"theMediaType","annotations":{"a1":{"keyNum": 13,"keyStr":"value1"},"a2":{"keyObj":{"subKey":"subVal"}}}}`

const badRd = `{"downloadLocation":"https://example.com/test.zip","mediaType":"theMediaType"}`

func createTestResourceDescriptor() (*ResourceDescriptor, error) {
// Create a ResourceDescriptor
a1, err := structpb.NewStruct(map[string]interface{}{
"keyStr": "value1",
"keyNum": 13})
if err != nil {
return nil, err
}
a2, err := structpb.NewStruct(map[string]interface{}{
"keyObj": map[string]interface{}{
"subKey": "subVal"}})
if err != nil {
return nil, err
}

return &ResourceDescriptor{
Name: "theName",
Uri: "https://example.com",
Digest: map[string]string{
"alg1": "abc123",
},
Content: []byte("bytescontent"),
DownloadLocation: "https://example.com/test.zip",
MediaType: "theMediaType",
Annotations: map[string]*structpb.Struct{"a1": a1, "a2": a2},
}, nil
}

func TestJsonUnmarshalResourceDescriptor(t *testing.T) {
got := &ResourceDescriptor{}
err := protojson.Unmarshal([]byte(wantFullRd), got)

assert.NoError(t, err, "Error during JSON unmarshalling")

want, err := createTestResourceDescriptor()

assert.NoError(t, err, "Error during test RD creation")
assert.True(t, proto.Equal(got, want), "Protos do not match")
}

func TestBadResourceDescriptor(t *testing.T) {
got := &ResourceDescriptor{}
err := protojson.Unmarshal([]byte(badRd), got)

assert.NoError(t, err, "Error during JSON unmarshalling")

err = got.Validate()

assert.Error(t, err, "Error: created malformed ResourceDescriptor")
}
42 changes: 42 additions & 0 deletions go/v1/statement.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
Wrapper APIs for in-toto attestation Statement layer protos.
*/

package v1

import "errors"

const StatementTypeUri = "https://in-toto.io/Statement/v1"

func (s *Statement) Validate() error {
if s.GetType() != StatementTypeUri {
return errors.New("Wrong statement type")
}

if s.GetSubject() == nil || len(s.GetSubject()) == 0 {
return errors.New("At least one subject required")
}

// check all resource descriptors in the subject
subject := s.GetSubject()
for _, rd := range subject {
if err := rd.Validate(); err != nil {
return err
}

// v1 statements require the digest to be set in the subject
if len(rd.GetDigest()) == 0 {
return errors.New("At least one digest required")
}
}

if s.GetPredicateType() == "" {
return errors.New("Predicate type required")
}

if s.GetPredicate() == nil {
return errors.New("Predicate object required")
}

return nil
}
Loading

0 comments on commit c7dc920

Please sign in to comment.