diff --git a/pkg/chains/signing.go b/pkg/chains/signing.go index 453bad9820..ce0bb380af 100644 --- a/pkg/chains/signing.go +++ b/pkg/chains/signing.go @@ -20,6 +20,7 @@ import ( "fmt" "github.com/hashicorp/go-multierror" + intoto "github.com/in-toto/attestation/go/v1" "github.com/tektoncd/chains/pkg/artifacts" "github.com/tektoncd/chains/pkg/chains/formats" "github.com/tektoncd/chains/pkg/chains/objects" @@ -29,6 +30,7 @@ import ( "github.com/tektoncd/chains/pkg/chains/storage" "github.com/tektoncd/chains/pkg/config" versioned "github.com/tektoncd/pipeline/pkg/client/clientset/versioned" + "google.golang.org/protobuf/encoding/protojson" "k8s.io/apimachinery/pkg/util/sets" "knative.dev/pkg/logging" ) @@ -169,7 +171,7 @@ func (o *ObjectSigner) Sign(ctx context.Context, tektonObj objects.TektonObject) } logger.Infof("Signing object with %s", signerType) - rawPayload, err := json.Marshal(payload) + rawPayload, err := getRawPayload(payload) if err != nil { logger.Warnf("Unable to marshal payload: %v", signerType, obj) continue @@ -248,3 +250,19 @@ func HandleRetry(ctx context.Context, obj objects.TektonObject, ps versioned.Int } return MarkFailed(ctx, obj, ps, annotations) } + +// getRawPayload returns the payload as a json string. If the given payload is a intoto.Statement type, protojson.Marshal +// is used to get the proper labels/field names in the resulting json. +func getRawPayload(payload interface{}) ([]byte, error) { + switch payloadObj := payload.(type) { + case intoto.Statement: + return protojson.Marshal(&payloadObj) + case *intoto.Statement: + if payloadObj == nil { + return json.Marshal(payload) + } + return protojson.Marshal(payloadObj) + default: + return json.Marshal(payload) + } +} diff --git a/pkg/chains/signing_test.go b/pkg/chains/signing_test.go index 0a8cba2f09..687953afe2 100644 --- a/pkg/chains/signing_test.go +++ b/pkg/chains/signing_test.go @@ -14,12 +14,16 @@ limitations under the License. package chains import ( + "bytes" "context" + "encoding/json" "errors" "fmt" "reflect" "testing" + "github.com/google/go-cmp/cmp" + intoto "github.com/in-toto/attestation/go/v1" "github.com/sigstore/rekor/pkg/generated/models" "github.com/tektoncd/chains/pkg/chains/objects" "github.com/tektoncd/chains/pkg/chains/signing" @@ -405,6 +409,84 @@ func TestSigningObjects(t *testing.T) { } } +func TestGetRawPayload(t *testing.T) { + tests := []struct { + name string + payload interface{} + expected string + }{ + { + name: "intoto.Statement object", + payload: intoto.Statement{ + Type: "type1", + PredicateType: "predicate-type1", + }, + expected: compactJSON(t, []byte(`{"_type":"type1","predicateType":"predicate-type1"}`)), + }, + { + name: "*intoto.Statement object", + payload: &intoto.Statement{ + Type: "type1", + PredicateType: "predicate-type1", + }, + expected: compactJSON(t, []byte(`{"_type":"type1","predicateType":"predicate-type1"}`)), + }, + { + name: "*intoto.Statement object - nil", + payload: (func() *intoto.Statement { return nil })(), + expected: "null", + }, + { + name: "other object - nil", + payload: nil, + expected: "null", + }, + { + name: "other object with value", + payload: struct { + Name string + ID int + Inner any + }{ + Name: "wrapper", + ID: 1, + Inner: struct { + InnerID int + Description string + IsArtifact bool + }{ + InnerID: 2, + Description: "some description", + IsArtifact: true, + }, + }, + expected: compactJSON(t, []byte(`{"Name":"wrapper","ID":1,"Inner": {"InnerID":2,"Description":"some description","IsArtifact":true}}`)), + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got, err := getRawPayload(test.payload) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + compactExpected := compactJSON(t, got) + if diff := cmp.Diff(test.expected, compactExpected); diff != "" { + t.Errorf("getRawPayload(), -want +got, diff = %s", diff) + } + }) + } +} + +func compactJSON(t *testing.T, jsonString []byte) string { + t.Helper() + dst := &bytes.Buffer{} + if err := json.Compact(dst, jsonString); err != nil { + t.Fatalf("error getting compact JSON: %v", err) + } + return dst.String() +} + func fakeAllBackends(backends []*mockBackend) map[string]storage.Backend { newBackends := map[string]storage.Backend{} for _, m := range backends { diff --git a/test/testdata/slsa/v1/pipeline-output-image.json b/test/testdata/slsa/v1/pipeline-output-image.json index 5a303c06c5..2c57b0090b 100644 --- a/test/testdata/slsa/v1/pipeline-output-image.json +++ b/test/testdata/slsa/v1/pipeline-output-image.json @@ -1,6 +1,6 @@ { - "type": "https://in-toto.io/Statement/v0.1", - "predicate_type": "https://slsa.dev/provenance/v0.2", + "_type": "https://in-toto.io/Statement/v0.1", + "predicateType": "https://slsa.dev/provenance/v0.2", "subject": [ { "name": "gcr.io/foo/bar", diff --git a/test/testdata/slsa/v1/task-output-image.json b/test/testdata/slsa/v1/task-output-image.json index c816782574..6f50533549 100644 --- a/test/testdata/slsa/v1/task-output-image.json +++ b/test/testdata/slsa/v1/task-output-image.json @@ -1,6 +1,6 @@ { - "type": "https://in-toto.io/Statement/v0.1", - "predicate_type": "https://slsa.dev/provenance/v0.2", + "_type": "https://in-toto.io/Statement/v0.1", + "predicateType": "https://slsa.dev/provenance/v0.2", "subject": [ { "name": "gcr.io/foo/bar", diff --git a/test/testdata/slsa/v2/task-output-image.json b/test/testdata/slsa/v2/task-output-image.json index aa4ebf9575..46bcf76a57 100644 --- a/test/testdata/slsa/v2/task-output-image.json +++ b/test/testdata/slsa/v2/task-output-image.json @@ -1,6 +1,6 @@ { - "type": "https://in-toto.io/Statement/v0.1", - "predicate_type": "https://slsa.dev/provenance/v0.2", + "_type": "https://in-toto.io/Statement/v0.1", + "predicateType": "https://slsa.dev/provenance/v0.2", "subject": [ { "name": "gcr.io/foo/bar", diff --git a/test/testdata/slsa/v2alpha3/pipeline-output-image.json b/test/testdata/slsa/v2alpha3/pipeline-output-image.json index 74ffc938d4..569c44a509 100644 --- a/test/testdata/slsa/v2alpha3/pipeline-output-image.json +++ b/test/testdata/slsa/v2alpha3/pipeline-output-image.json @@ -1,6 +1,6 @@ { - "type": "https://in-toto.io/Statement/v1", - "predicate_type": "https://slsa.dev/provenance/v1", + "_type": "https://in-toto.io/Statement/v1", + "predicateType": "https://slsa.dev/provenance/v1", "subject": [ { "name": "gcr.io/foo/bar", diff --git a/test/testdata/slsa/v2alpha3/task-output-image.json b/test/testdata/slsa/v2alpha3/task-output-image.json index c8e8956401..bb092330d4 100644 --- a/test/testdata/slsa/v2alpha3/task-output-image.json +++ b/test/testdata/slsa/v2alpha3/task-output-image.json @@ -1,6 +1,6 @@ { - "type": "https://in-toto.io/Statement/v1", - "predicate_type": "https://slsa.dev/provenance/v1", + "_type": "https://in-toto.io/Statement/v1", + "predicateType": "https://slsa.dev/provenance/v1", "subject": [ { "name": "gcr.io/foo/bar", diff --git a/test/testdata/slsa/v2alpha4/pipeline-output-image.json b/test/testdata/slsa/v2alpha4/pipeline-output-image.json index 03a2cd2639..d7ae8848de 100644 --- a/test/testdata/slsa/v2alpha4/pipeline-output-image.json +++ b/test/testdata/slsa/v2alpha4/pipeline-output-image.json @@ -1,6 +1,6 @@ { - "type": "https://in-toto.io/Statement/v1", - "predicate_type": "https://slsa.dev/provenance/v1", + "_type": "https://in-toto.io/Statement/v1", + "predicateType": "https://slsa.dev/provenance/v1", "subject": [ { "name": "gcr.io/foo/bar", diff --git a/test/testdata/slsa/v2alpha4/pipeline-with-object-type-hinting.json b/test/testdata/slsa/v2alpha4/pipeline-with-object-type-hinting.json index 3dbc87d1cf..704f896f34 100644 --- a/test/testdata/slsa/v2alpha4/pipeline-with-object-type-hinting.json +++ b/test/testdata/slsa/v2alpha4/pipeline-with-object-type-hinting.json @@ -1,6 +1,6 @@ { - "type": "https://in-toto.io/Statement/v1", - "predicate_type": "https://slsa.dev/provenance/v1", + "_type": "https://in-toto.io/Statement/v1", + "predicateType": "https://slsa.dev/provenance/v1", "subject": [ { "name": "gcr.io/foo/img1", diff --git a/test/testdata/slsa/v2alpha4/task-output-image.json b/test/testdata/slsa/v2alpha4/task-output-image.json index 440ea595f3..d16164077c 100644 --- a/test/testdata/slsa/v2alpha4/task-output-image.json +++ b/test/testdata/slsa/v2alpha4/task-output-image.json @@ -1,6 +1,6 @@ { - "type": "https://in-toto.io/Statement/v1", - "predicate_type": "https://slsa.dev/provenance/v1", + "_type": "https://in-toto.io/Statement/v1", + "predicateType": "https://slsa.dev/provenance/v1", "subject": [ { "name": "gcr.io/foo/bar", diff --git a/test/testdata/slsa/v2alpha4/task-with-object-type-hinting.json b/test/testdata/slsa/v2alpha4/task-with-object-type-hinting.json index 9f835644a3..a8c156330d 100644 --- a/test/testdata/slsa/v2alpha4/task-with-object-type-hinting.json +++ b/test/testdata/slsa/v2alpha4/task-with-object-type-hinting.json @@ -1,6 +1,6 @@ { - "type": "https://in-toto.io/Statement/v1", - "predicate_type": "https://slsa.dev/provenance/v1", + "_type": "https://in-toto.io/Statement/v1", + "predicateType": "https://slsa.dev/provenance/v1", "subject": [ { "name": "gcr.io/foo/img2",