Skip to content

Commit b6cf9cb

Browse files
authored
chore(attestation): extract envelope renderer utils to shared package (#48)
Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>
1 parent ff27885 commit b6cf9cb

File tree

6 files changed

+225
-40
lines changed

6 files changed

+225
-40
lines changed

app/controlplane/internal/service/attestation.go

Lines changed: 2 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import (
2626
"time"
2727

2828
"github.com/cenkalti/backoff/v4"
29-
"github.com/in-toto/in-toto-golang/in_toto"
3029

3130
cpAPI "github.com/chainloop-dev/chainloop/app/controlplane/api/controlplane/v1"
3231
contractAPI "github.com/chainloop-dev/chainloop/app/controlplane/api/workflowcontract/v1"
@@ -295,36 +294,13 @@ func (s *AttestationService) GetUploadCreds(ctx context.Context, _ *cpAPI.Attest
295294
return &cpAPI.AttestationServiceGetUploadCredsResponse{Result: &cpAPI.AttestationServiceGetUploadCredsResponse_Result{Token: t}}, nil
296295
}
297296

298-
func extractPredicate(envelope *dsse.Envelope) (*renderer.ChainloopProvenancePredicateV1, error) {
299-
decodedPayload, err := envelope.DecodeB64Payload()
300-
if err != nil {
301-
return nil, err
302-
}
303-
304-
statement := &in_toto.Statement{}
305-
if err := json.Unmarshal(decodedPayload, statement); err != nil {
306-
return nil, fmt.Errorf("un-marshaling predicate: %w", err)
307-
}
308-
309-
var predicate *renderer.ChainloopProvenancePredicateV1
310-
if statement.PredicateType == renderer.ChainloopPredicateTypeV1 {
311-
if predicate, err = extractPredicateV1(statement); err != nil {
312-
return nil, fmt.Errorf("extracting predicate: %w", err)
313-
}
314-
} else {
315-
return nil, errors.InternalServer("internal error", "predicate type not supported")
316-
}
317-
318-
return predicate, nil
319-
}
320-
321297
func bizAttestationToPb(att *biz.Attestation) (*cpAPI.AttestationItem, error) {
322298
encodedAttestation, err := json.Marshal(att.Envelope)
323299
if err != nil {
324300
return nil, err
325301
}
326302

327-
predicate, err := extractPredicate(att.Envelope)
303+
predicate, err := renderer.ExtractPredicate(att.Envelope)
328304
if err != nil {
329305
return nil, err
330306
}
@@ -358,20 +334,6 @@ func extractMaterials(in []*renderer.ChainloopProvenanceMaterial) []*cpAPI.Attes
358334
return res
359335
}
360336

361-
func extractPredicateV1(statement *in_toto.Statement) (*renderer.ChainloopProvenancePredicateV1, error) {
362-
jsonPredicate, err := json.Marshal(statement.Predicate)
363-
if err != nil {
364-
return nil, fmt.Errorf("un-marshaling predicate: %w", err)
365-
}
366-
367-
predicate := &renderer.ChainloopProvenancePredicateV1{}
368-
if err := json.Unmarshal(jsonPredicate, predicate); err != nil {
369-
return nil, fmt.Errorf("un-marshaling predicate: %w", err)
370-
}
371-
372-
return predicate, nil
373-
}
374-
375337
type uploadSBOMToDepTrackOpts struct {
376338
envelope *dsse.Envelope
377339
orgID, workflowID string
@@ -410,7 +372,7 @@ func uploadSBOMsToDependencyTrack(opts *uploadSBOMToDepTrackOpts) error {
410372
return nil
411373
}
412374

413-
predicate, err := extractPredicate(opts.envelope)
375+
predicate, err := renderer.ExtractPredicate(opts.envelope)
414376
if err != nil {
415377
return err
416378
}

internal/attestation/renderer/chainloop.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424

2525
v1 "github.com/chainloop-dev/chainloop/app/cli/api/attestation/v1"
2626
"github.com/in-toto/in-toto-golang/in_toto"
27+
"github.com/secure-systems-lab/go-securesystemslib/dsse"
2728

2829
schemaapi "github.com/chainloop-dev/chainloop/app/controlplane/api/workflowcontract/v1"
2930
slsacommon "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common"
@@ -213,3 +214,44 @@ func outputChainloopMaterials(att *v1.Attestation, onlyOutput bool) []*Chainloop
213214

214215
return res
215216
}
217+
218+
// Extract the Chainloop attestation predicate from an encoded DSSE envelope
219+
func ExtractPredicate(envelope *dsse.Envelope) (*ChainloopProvenancePredicateV1, error) {
220+
decodedPayload, err := envelope.DecodeB64Payload()
221+
if err != nil {
222+
return nil, err
223+
}
224+
225+
// 1 - Extract the in-toto statement
226+
statement := &in_toto.Statement{}
227+
if err := json.Unmarshal(decodedPayload, statement); err != nil {
228+
return nil, fmt.Errorf("un-marshaling predicate: %w", err)
229+
}
230+
231+
// 2 - Extract the Chainloop predicate from the in-toto statement
232+
var predicate *ChainloopProvenancePredicateV1
233+
switch statement.PredicateType {
234+
case ChainloopPredicateTypeV1:
235+
if predicate, err = extractPredicateV1(statement); err != nil {
236+
return nil, fmt.Errorf("extracting predicate: %w", err)
237+
}
238+
default:
239+
return nil, fmt.Errorf("unsupported predicate type: %s", statement.PredicateType)
240+
}
241+
242+
return predicate, nil
243+
}
244+
245+
func extractPredicateV1(statement *in_toto.Statement) (*ChainloopProvenancePredicateV1, error) {
246+
jsonPredicate, err := json.Marshal(statement.Predicate)
247+
if err != nil {
248+
return nil, fmt.Errorf("un-marshaling predicate: %w", err)
249+
}
250+
251+
predicate := &ChainloopProvenancePredicateV1{}
252+
if err := json.Unmarshal(jsonPredicate, predicate); err != nil {
253+
return nil, fmt.Errorf("un-marshaling predicate: %w", err)
254+
}
255+
256+
return predicate, nil
257+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
//
2+
// Copyright 2023 The Chainloop Authors.
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
package renderer
17+
18+
import (
19+
"encoding/json"
20+
"fmt"
21+
"os"
22+
"testing"
23+
24+
"github.com/secure-systems-lab/go-securesystemslib/dsse"
25+
"github.com/stretchr/testify/assert"
26+
"github.com/stretchr/testify/require"
27+
)
28+
29+
func TestExtractPredicate(t *testing.T) {
30+
testCases := []struct {
31+
name string
32+
envelopePath string
33+
predicatePath string
34+
wantErr bool
35+
}{
36+
{
37+
name: "valid envelope",
38+
envelopePath: "testdata/valid.envelope.json",
39+
predicatePath: "testdata/valid.predicate.json",
40+
wantErr: false,
41+
},
42+
{
43+
name: "unknown source attestation",
44+
envelopePath: "testdata/unknown.envelope.json",
45+
wantErr: true,
46+
},
47+
}
48+
49+
for _, tc := range testCases {
50+
t.Run(tc.name, func(t *testing.T) {
51+
envelope, err := testEnvelope(tc.envelopePath)
52+
require.NoError(t, err)
53+
54+
got, err := ExtractPredicate(envelope)
55+
if tc.wantErr {
56+
assert.Error(t, err)
57+
return
58+
}
59+
60+
want, err := testPredicate(tc.predicatePath)
61+
require.NoError(t, err)
62+
63+
assert.NoError(t, err)
64+
assert.Equal(t, want, got)
65+
})
66+
}
67+
}
68+
69+
func testEnvelope(filePath string) (*dsse.Envelope, error) {
70+
var envelope dsse.Envelope
71+
content, err := os.ReadFile(filePath)
72+
if err != nil {
73+
return nil, err
74+
}
75+
76+
err = json.Unmarshal(content, &envelope)
77+
if err != nil {
78+
return nil, err
79+
}
80+
81+
return &envelope, nil
82+
}
83+
84+
func testPredicate(path string) (*ChainloopProvenancePredicateV1, error) {
85+
var predicate ChainloopProvenancePredicateV1
86+
content, err := os.ReadFile(path)
87+
if err != nil {
88+
return nil, err
89+
}
90+
91+
if err := json.Unmarshal(content, &predicate); err != nil {
92+
return nil, fmt.Errorf("un-marshaling predicate: %w", err)
93+
}
94+
95+
return &predicate, nil
96+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"payloadType": "application/vnd.in-toto+json",
3+
"payload": "eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjAuMSIsInByZWRpY2F0ZVR5cGUiOiJ1bmtub3duLmNvbXBhbnkvYXR0ZXN0YXRpb24vdjAuMSIsInN1YmplY3QiOlt7Im5hbWUiOiJjaGFpbmxvb3AuZGV2L3dvcmtmbG93L2ZvbyIsImRpZ2VzdCI6eyJzaGEyNTYiOiIzMGE2ZTQ3NzAyMTQ4ZWE4NTUzYjNlOGZhYjA1OTUyYzMyOTdhNzMwM2EwZWY4MjA1ODAxM2U4NTg0NGFjYmQzIn19XSwicHJlZGljYXRlIjp7Im1ldGFkYXRhIjp7Im5hbWUiOiJmb28iLCJwcm9qZWN0IjoiYmFyIiwidGVhbSI6IiIsImluaXRpYWxpemVkQXQiOiIyMDIzLTAzLTIyVDEwOjE1OjQwLjczNTUzMTg0MVoiLCJmaW5pc2hlZEF0IjoiMjAyMy0wMy0yMlQxMToxNjoxOS45Njg0ODg4MzcrMDE6MDAiLCJ3b3JrZmxvd1J1bklEIjoiYTc3ZDhlNGUtNDE1My00ZWVlLTk4ZWQtN2E2MDg4OTNmMzFiIiwid29ya2Zsb3dJRCI6Ijk0YTgwOTcyLTA0YjAtNDIxNS1hZDk4LWE3NGQwODI2M2Y5ZiJ9LCJidWlsZGVyIjp7ImlkIjoiY2hhaW5sb29wLmRldi9jbGkvZGV2QHNoYTI1Njo2M2I2MDAxZjczY2Y3ZDI4ZGVjMzc3MjBlOGUwYjJmNzc5YTY5MDE4ZTgwYmRhY2JhNzY2ZDAxYjU5MDEwOThiIn0sImJ1aWxkVHlwZSI6ImNoYWlubG9vcC5kZXYvd29ya2Zsb3dydW4vdjAuMSIsInJ1bm5lclR5cGUiOiJSVU5ORVJfVFlQRV9VTlNQRUNJRklFRCJ9fQ==",
4+
"signatures": [
5+
{
6+
"keyid": "",
7+
"sig": "MEQCICuT3+A0ub9e3VJ/wjPv+oPTnwUR1AT3IKlXyqUXcgphAiAOeX1hUNYM+rGl6NE1sbTwqag6unuuXceAZF3aNCJ9YQ=="
8+
}
9+
]
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"payloadType": "application/vnd.in-toto+json",
3+
"payload": "eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjAuMSIsInByZWRpY2F0ZVR5cGUiOiJjaGFpbmxvb3AuZGV2L2F0dGVzdGF0aW9uL3YwLjEiLCJzdWJqZWN0IjpbeyJuYW1lIjoiY2hhaW5sb29wLmRldi93b3JrZmxvdy9idWlsZC1hbmQtcmVsZWFzZSIsImRpZ2VzdCI6eyJzaGEyNTYiOiI0YzQ2YTYzOWU5ZDM5NWJhYjMyODk1NDk1YjNkODExODZmNzhlNTMxMGFjM2RhODU0Mzk0ZjgxOWExMDMxYzk3In19LHsibmFtZSI6ImdoY3IuaW8vY2hhaW5sb29wLWRldi9pbnRlZ3JhdGlvbi1kZW1vIiwiZGlnZXN0Ijp7InNoYTI1NiI6ImUwZDgxNzk5OTFkZDczNWJhZjA5NjE5MDFiMzM0NzZhNzZhMGYzMDBiYzRlYTA3ZTNkN2FlN2MyNGUxNDcxOTMifX1dLCJwcmVkaWNhdGUiOnsibWV0YWRhdGEiOnsibmFtZSI6ImJ1aWxkLWFuZC1yZWxlYXNlIiwicHJvamVjdCI6ImludGVncmF0aW9uLWRlbW8iLCJ0ZWFtIjoiIiwiaW5pdGlhbGl6ZWRBdCI6IjIwMjMtMDMtMTNUMjM6MzU6MzYuOTgzMTM4OTE5WiIsImZpbmlzaGVkQXQiOiIyMDIzLTAzLTEzVDIzOjM2OjM2Ljc0OTMyNTEwM1oiLCJ3b3JrZmxvd1J1bklEIjoiOTEwOTNmNmUtMzdjMC00ZTAyLWFmNWQtYzk4NTk1OTZkMTI1Iiwid29ya2Zsb3dJRCI6IjM3MDIyYjJmLTM0YzMtNGY0Ny05ZmQ3LTUxNGI0YTdiYWFhZCJ9LCJtYXRlcmlhbHMiOlt7Im5hbWUiOiJiaW5hcnkiLCJ0eXBlIjoiQVJUSUZBQ1QiLCJtYXRlcmlhbCI6eyJzbHNhIjp7InVyaSI6ImludGVncmF0aW9uLWRlbW9fMC4wLjM5X2xpbnV4X2FtZDY0LnRhci5neiIsImRpZ2VzdCI6eyJzaGEyNTYiOiJiMTU1Y2RmYzMyOGIyNzNjNGI3NDFjMDhiM2I4NGFjNDQxYjA1NjJjYTUxODkzZjIzNDk1YjM1YWJmODllYTg3In19fX0seyJuYW1lIjoiaW1hZ2UiLCJ0eXBlIjoiQ09OVEFJTkVSX0lNQUdFIiwibWF0ZXJpYWwiOnsic2xzYSI6eyJ1cmkiOiJnaGNyLmlvL2NoYWlubG9vcC1kZXYvaW50ZWdyYXRpb24tZGVtbyIsImRpZ2VzdCI6eyJzaGEyNTYiOiJlMGQ4MTc5OTkxZGQ3MzViYWYwOTYxOTAxYjMzNDc2YTc2YTBmMzAwYmM0ZWEwN2UzZDdhZTdjMjRlMTQ3MTkzIn19fX0seyJuYW1lIjoic2JvbSIsInR5cGUiOiJTQk9NX0NZQ0xPTkVEWF9KU09OIiwibWF0ZXJpYWwiOnsic2xzYSI6eyJ1cmkiOiJzYm9tLmN5Y2xvbmVkeC5qc29uIiwiZGlnZXN0Ijp7InNoYTI1NiI6ImI1MGYzODk2MWNjMmU5N2QwOTAzZjQ2ODNhNDBlMjUyOGY3ZjZjOWQzODJlOGM2MDQ4YjAzNjNhZjk1YjcwODAifX19fV0sImJ1aWxkZXIiOnsiaWQiOiJjaGFpbmxvb3AuZGV2L2NsaS8wLjguOTJAc2hhMjU2OmZhMDFjNmNkMTA0ODAzZWQ1YjFlODBlMjE2YzFjN2Y2MjQ0YjIxY2VmODVjYTQ4YmI2OTMwN2JhMTcxM2U2MTkifSwiYnVpbGRUeXBlIjoiY2hhaW5sb29wLmRldi93b3JrZmxvd3J1bi92MC4xIiwiZW52Ijp7IkdJVEhVQl9BQ1RPUiI6Im1pZ21hcnRyaSIsIkdJVEhVQl9SRUYiOiJyZWZzL3RhZ3MvdjAuMC4zOSIsIkdJVEhVQl9SRVBPU0lUT1JZIjoiY2hhaW5sb29wLWRldi9pbnRlZ3JhdGlvbi1kZW1vIiwiR0lUSFVCX1JFUE9TSVRPUllfT1dORVIiOiJjaGFpbmxvb3AtZGV2IiwiR0lUSFVCX1JVTl9JRCI6IjQ0MTA1NDMzNjUiLCJHSVRIVUJfU0hBIjoiMGFjY2M5MzkyZmIxZjliMjU4MTY3YzE4ZmZhMGFlYjYyNjk3M2YxYyIsIlJVTk5FUl9OQU1FIjoiSG9zdGVkIEFnZW50IiwiUlVOTkVSX09TIjoiTGludXgifSwicnVubmVyVHlwZSI6IkdJVEhVQl9BQ1RJT04iLCJydW5uZXJVUkwiOiJodHRwczovL2dpdGh1Yi5jb20vY2hhaW5sb29wLWRldi9pbnRlZ3JhdGlvbi1kZW1vL2FjdGlvbnMvcnVucy80NDEwNTQzMzY1In19",
4+
"signatures": [
5+
{
6+
"keyid": "",
7+
"sig": "MEQCIBlSdPt604OSyDMF4vjY8DoKQ6uxc1NADtay0q4Ii4f7AiA4KxaiIc1HPSi0a0bJv3l0V/wR2aPvxsDDfmAEd+LFTA=="
8+
}
9+
]
10+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
{
2+
"buildType": "chainloop.dev/workflowrun/v0.1",
3+
"builder": {
4+
"id": "chainloop.dev/cli/0.8.92@sha256:fa01c6cd104803ed5b1e80e216c1c7f6244b21cef85ca48bb69307ba1713e619"
5+
},
6+
"env": {
7+
"GITHUB_ACTOR": "migmartri",
8+
"GITHUB_REF": "refs/tags/v0.0.39",
9+
"GITHUB_REPOSITORY": "chainloop-dev/integration-demo",
10+
"GITHUB_REPOSITORY_OWNER": "chainloop-dev",
11+
"GITHUB_RUN_ID": "4410543365",
12+
"GITHUB_SHA": "0accc9392fb1f9b258167c18ffa0aeb626973f1c",
13+
"RUNNER_NAME": "Hosted Agent",
14+
"RUNNER_OS": "Linux"
15+
},
16+
"materials": [
17+
{
18+
"material": {
19+
"slsa": {
20+
"digest": {
21+
"sha256": "b155cdfc328b273c4b741c08b3b84ac441b0562ca51893f23495b35abf89ea87"
22+
},
23+
"uri": "integration-demo_0.0.39_linux_amd64.tar.gz"
24+
}
25+
},
26+
"name": "binary",
27+
"type": "ARTIFACT"
28+
},
29+
{
30+
"material": {
31+
"slsa": {
32+
"digest": {
33+
"sha256": "e0d8179991dd735baf0961901b33476a76a0f300bc4ea07e3d7ae7c24e147193"
34+
},
35+
"uri": "ghcr.io/chainloop-dev/integration-demo"
36+
}
37+
},
38+
"name": "image",
39+
"type": "CONTAINER_IMAGE"
40+
},
41+
{
42+
"material": {
43+
"slsa": {
44+
"digest": {
45+
"sha256": "b50f38961cc2e97d0903f4683a40e2528f7f6c9d382e8c6048b0363af95b7080"
46+
},
47+
"uri": "sbom.cyclonedx.json"
48+
}
49+
},
50+
"name": "sbom",
51+
"type": "SBOM_CYCLONEDX_JSON"
52+
}
53+
],
54+
"metadata": {
55+
"finishedAt": "2023-03-13T23:36:36.749325103Z",
56+
"initializedAt": "2023-03-13T23:35:36.983138919Z",
57+
"name": "build-and-release",
58+
"project": "integration-demo",
59+
"team": "",
60+
"workflowID": "37022b2f-34c3-4f47-9fd7-514b4a7baaad",
61+
"workflowRunID": "91093f6e-37c0-4e02-af5d-c9859596d125"
62+
},
63+
"runnerType": "GITHUB_ACTION",
64+
"runnerURL": "https://github.com/chainloop-dev/integration-demo/actions/runs/4410543365"
65+
}

0 commit comments

Comments
 (0)