From 938abe24cc2d1541c965c14973d797770ddfebf4 Mon Sep 17 00:00:00 2001 From: Andrew Gillis Date: Fri, 1 Nov 2024 10:49:51 -0400 Subject: [PATCH 1/4] Add custom marshal/unmarshal, fixes #363, see sigstore/sigstore-go#326 Signed-off-by: Andrew Gillis --- go.mod | 4 +++- go.sum | 2 ++ go/v1/statement.go | 14 +++++++++++++- go/v1/statement_test.go | 36 ++++++++++++++++++++++++++++++++++-- 4 files changed, 52 insertions(+), 4 deletions(-) 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..2b4e8b1e 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) +} \ No newline at end of file diff --git a/go/v1/statement_test.go b/go/v1/statement_test.go index 1c90d1c8..120c2837 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,38 @@ 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 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"}}}` @@ -62,6 +91,8 @@ func TestBadStatementType(t *testing.T) { assert.ErrorIs(t, err, ErrInvalidStatementType, "created malformed Statement (bad type)") } + + func TestBadStatementSubject(t *testing.T) { tests := map[string]struct { input string @@ -122,3 +153,4 @@ func TestBadStatementPredicate(t *testing.T) { assert.ErrorIs(t, err, test.err, fmt.Sprintf("%s in test '%s'", test.noErrMessage, name)) } } + From 5c730dc76f89fabec3f2efeaa376353923cabc18 Mon Sep 17 00:00:00 2001 From: Andrew Gillis Date: Fri, 1 Nov 2024 11:05:20 -0400 Subject: [PATCH 2/4] Fix format Signed-off-by: Andrew Gillis --- go/v1/statement.go | 2 +- go/v1/statement_test.go | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/go/v1/statement.go b/go/v1/statement.go index 2b4e8b1e..2868d1ba 100644 --- a/go/v1/statement.go +++ b/go/v1/statement.go @@ -59,4 +59,4 @@ func (s *Statement) MarshalJSON() ([]byte, error) { func (s *Statement) UnmarshalJSON(data []byte) error { return protojson.Unmarshal(data, s) -} \ No newline at end of file +} diff --git a/go/v1/statement_test.go b/go/v1/statement_test.go index 120c2837..25632664 100644 --- a/go/v1/statement_test.go +++ b/go/v1/statement_test.go @@ -91,8 +91,6 @@ func TestBadStatementType(t *testing.T) { assert.ErrorIs(t, err, ErrInvalidStatementType, "created malformed Statement (bad type)") } - - func TestBadStatementSubject(t *testing.T) { tests := map[string]struct { input string @@ -153,4 +151,3 @@ func TestBadStatementPredicate(t *testing.T) { assert.ErrorIs(t, err, test.err, fmt.Sprintf("%s in test '%s'", test.noErrMessage, name)) } } - From e0f79adad3b5b8d75993a77e4db96008e0fa849f Mon Sep 17 00:00:00 2001 From: Andrew Gillis Date: Wed, 20 Nov 2024 14:03:25 -0500 Subject: [PATCH 3/4] fix: update ci to use version from go.mod file Signed-off-by: Andrew Gillis --- .github/workflows/run-go-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From a64ec07066010766f0be57ec093292ae75d67ced Mon Sep 17 00:00:00 2001 From: Andrew Gillis Date: Wed, 20 Nov 2024 17:04:29 -0500 Subject: [PATCH 4/4] add test for unmarshal json using proto field names Signed-off-by: Andrew Gillis --- go/v1/statement_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/go/v1/statement_test.go b/go/v1/statement_test.go index 25632664..529675be 100644 --- a/go/v1/statement_test.go +++ b/go/v1/statement_test.go @@ -62,6 +62,14 @@ func TestJsonUnmarshalStatement(t *testing.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)