diff --git a/.github/workflows/run-go-tests.yml b/.github/workflows/run-go-tests.yml index dd5ae004..68293cf9 100644 --- a/.github/workflows/run-go-tests.yml +++ b/.github/workflows/run-go-tests.yml @@ -22,7 +22,7 @@ jobs: - name: Install Go uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 with: - go-version: 1.20.x + go-version-file: './go.mod' - name: Format run: if [ "$(gofmt -s -l . | wc -l)" -gt 0 ]; then exit 1; fi - name: Setup Env diff --git a/go.mod b/go.mod index 157a4d5b..782b9e66 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,8 @@ module github.com/in-toto/attestation -go 1.20 +go 1.21 + +toolchain go1.21.0 require ( github.com/stretchr/testify v1.9.0 diff --git a/go.sum b/go.sum index 348ec566..e70d7cad 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,13 @@ 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/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/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 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.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= diff --git a/go/v1/statement.go b/go/v1/statement.go index f63d5f0d..2868d1ba 100644 --- a/go/v1/statement.go +++ b/go/v1/statement.go @@ -4,7 +4,11 @@ Wrapper APIs for in-toto attestation Statement layer protos. package v1 -import "errors" +import ( + "errors" + + "google.golang.org/protobuf/encoding/protojson" +) const StatementTypeUri = "https://in-toto.io/Statement/v1" @@ -48,3 +52,11 @@ func (s *Statement) Validate() error { return nil } + +func (s *Statement) MarshalJSON() ([]byte, error) { + return protojson.Marshal(s) +} + +func (s *Statement) UnmarshalJSON(data []byte) error { + return protojson.Unmarshal(data, s) +} diff --git a/go/v1/statement_test.go b/go/v1/statement_test.go index 1c90d1c8..529675be 100644 --- a/go/v1/statement_test.go +++ b/go/v1/statement_test.go @@ -5,6 +5,7 @@ Tests for in-toto attestation ResourceDescriptor protos. package v1 import ( + "encoding/json" "fmt" "testing" @@ -39,7 +40,7 @@ func createTestStatement(t *testing.T) *Statement { } } -func TestJsonUnmarshalStatement(t *testing.T) { +func TestProtojsonUnmarshalStatement(t *testing.T) { var wantSt = `{"_type":"https://in-toto.io/Statement/v1","subject":[{"name":"theSub","digest":{"alg1":"abc123"}}],"predicateType":"thePredicate","predicate":{"keyObj":{"subKey":"subVal"}}}` got := &Statement{} @@ -47,10 +48,46 @@ func TestJsonUnmarshalStatement(t *testing.T) { assert.NoError(t, err, "error during JSON unmarshalling") want := createTestStatement(t) - assert.NoError(t, err, "unexpected error during test Statement creation") assert.True(t, proto.Equal(got, want), "protos do not match") } +func TestJsonUnmarshalStatement(t *testing.T) { + var wantSt = `{"_type":"https://in-toto.io/Statement/v1","subject":[{"name":"theSub","digest":{"alg1":"abc123"}}],"predicateType":"thePredicate","predicate":{"keyObj":{"subKey":"subVal"}}}` + + got := &Statement{} + err := json.Unmarshal([]byte(wantSt), got) + assert.NoError(t, err, "error during JSON unmarshalling") + + want := createTestStatement(t) + assert.True(t, proto.Equal(got, want), "protos do not match") +} + +func TestProtoNamesJsonUnmarshalStatementError(t *testing.T) { + // Note the use of proto field names in the JSON (e.g. "type" instead of "_type") + var wantSt = `{"type":"https://in-toto.io/Statement/v1","subject":[{"name":"theSub","digest":{"alg1":"abc123"}}],"predicate_type":"thePredicate","predicate":{"keyObj":{"subKey":"subVal"}}}` + got := &Statement{} + err := json.Unmarshal([]byte(wantSt), got) + assert.Error(t, err, "error is not thrown when proto field names are used in JSON") +} + +func TestProtojsonMarshalStatement(t *testing.T) { + var wantSt = `{"_type":"https://in-toto.io/Statement/v1","subject":[{"name":"theSub","digest":{"alg1":"abc123"}}],"predicateType":"thePredicate","predicate":{"keyObj":{"subKey":"subVal"}}}` + want := createTestStatement(t) + + gotSt, err := protojson.Marshal(want) + assert.NoError(t, err, "error during JSON marshalling") + assert.JSONEq(t, wantSt, string(gotSt), "JSON objects do not match") +} + +func TestJsonMarshalStatement(t *testing.T) { + var wantSt = `{"_type":"https://in-toto.io/Statement/v1","subject":[{"name":"theSub","digest":{"alg1":"abc123"}}],"predicateType":"thePredicate","predicate":{"keyObj":{"subKey":"subVal"}}}` + want := createTestStatement(t) + + gotSt, err := json.Marshal(want) + assert.NoError(t, err, "error during JSON marshalling") + assert.JSONEq(t, wantSt, string(gotSt), "JSON objects do not match") +} + func TestBadStatementType(t *testing.T) { var badStType = `{"_type":"https://in-toto.io/Statement/v0","subject":[{"name":"theSub","digest":{"alg1":"abc123"}}],"predicateType":"thePredicate","predicate":{"keyObj":{"subKey":"subVal"}}}`