Skip to content

Commit cc9e3c3

Browse files
authored
feat: CSAF VEX / OpenVEX material type support (#356)
Signed-off-by: Miguel Martinez Trivino <[email protected]>
1 parent 470d178 commit cc9e3c3

File tree

13 files changed

+707
-58
lines changed

13 files changed

+707
-58
lines changed

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,21 @@ SecOps are the ones in charge of defining the Workflow Contracts, setting up thi
118118

119119
Development teams on the other hand, just need to integrate Chainloop's jargon-free [crafting tool](https://docs.chainloop.dev/getting-started/attestation-crafting) and follow the steps via a familiar DevExp to make sure they comply with the Workflow Contract defined by the SecOps team. No need to learn in-toto, signing, SLSA, OCI, APIs, nada :)
120120

121+
## Supported Pieces of Evidence / Materials
122+
123+
Chainloop supports the collection of the following pieces of evidence types:
124+
125+
- [Container Image Reference](https://github.com/opencontainers/image-spec)
126+
- [CycloneDX SBOM](https://github.com/CycloneDX/specification)
127+
- [SPDX SBOM](https://spdx.dev/specifications/)
128+
- [CSAF VEX](https://docs.oasis-open.org/csaf/csaf/v2.0/os/csaf-v2.0-os.html)
129+
- [OpenVEX](https://github.com/openvex)
130+
- [JUnit](https://www.ibm.com/docs/en/developer-for-zos/14.1?topic=formats-junit-xml-format)
131+
- Generic Artifact Types
132+
- Key-Value metadata pairs
133+
134+
During the attestation process, these pieces of evidence will get uploaded to the [Content Addressable Storage](https://docs.chainloop.dev/reference/operator/cas-backend/) (if applicable) and referenced in a [SLSA](https://slsa.dev) attestation.
135+
121136
## Documentation
122137

123138
To learn more, please visit the Chainloop project's documentation website, https://docs.chainloop.dev where you will find a getting started guide, FAQ, examples, and more.

app/controlplane/api/gen/frontend/workflowcontract/v1/crafting_schema.ts

Lines changed: 14 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/controlplane/api/workflowcontract/v1/crafting_schema.pb.go

Lines changed: 25 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/controlplane/api/workflowcontract/v1/crafting_schema.proto

Lines changed: 49 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -17,59 +17,66 @@ syntax = "proto3";
1717

1818
package workflowcontract.v1;
1919

20-
option go_package = "github.com/chainloop-dev/chainloop/app/controlplane/api/workflowcontract/v1";
21-
2220
import "validate/validate.proto";
2321

22+
option go_package = "github.com/chainloop-dev/chainloop/app/controlplane/api/workflowcontract/v1";
23+
2424
// Schema definition provided by the user to the tool
2525
// that defines the schema of the workflowRun
2626
message CraftingSchema {
27-
// Version of the schema, do not confuse with the revision of the content
28-
string schema_version = 1 [(validate.rules).string.const = "v1"];
29-
repeated Material materials = 2;
30-
repeated string env_allow_list = 3;
31-
Runner runner = 4;
32-
// List of annotations that can be used to add metadata to the attestation
33-
// this metadata can be used later on by the integrations engine to filter and interpolate data
34-
// It works in addition to the annotations defined in the materials and the runner
35-
repeated Annotation annotations = 5;
27+
// Version of the schema, do not confuse with the revision of the content
28+
string schema_version = 1 [(validate.rules).string.const = "v1"];
29+
repeated Material materials = 2;
30+
repeated string env_allow_list = 3;
31+
Runner runner = 4;
32+
// List of annotations that can be used to add metadata to the attestation
33+
// this metadata can be used later on by the integrations engine to filter and interpolate data
34+
// It works in addition to the annotations defined in the materials and the runner
35+
repeated Annotation annotations = 5;
3636

37-
message Runner {
38-
RunnerType type = 1 [(validate.rules).enum = {not_in: [0]}];
37+
message Runner {
38+
RunnerType type = 1 [(validate.rules).enum = {
39+
not_in: [0]
40+
}];
3941

40-
enum RunnerType {
41-
RUNNER_TYPE_UNSPECIFIED = 0;
42-
GITHUB_ACTION = 1;
43-
GITLAB_PIPELINE = 2;
44-
AZURE_PIPELINE = 3;
45-
}
42+
enum RunnerType {
43+
RUNNER_TYPE_UNSPECIFIED = 0;
44+
GITHUB_ACTION = 1;
45+
GITLAB_PIPELINE = 2;
46+
AZURE_PIPELINE = 3;
4647
}
48+
}
4749

48-
message Material {
49-
MaterialType type = 1 [(validate.rules).enum = {not_in: [0]}];
50-
string name = 2 [(validate.rules).string.pattern = "^[\\w|-]+$"]; // Single word optionally separated with _ or -
51-
bool optional = 3;
52-
// If a material is set as output it will get added to the subject in the statement
53-
bool output = 4;
54-
// List of annotations that can be used to add metadata to the material
55-
// this metadata can be used later on by the integrations engine to filter and interpolate data
56-
repeated Annotation annotations = 5;
50+
message Material {
51+
MaterialType type = 1 [(validate.rules).enum = {
52+
not_in: [0]
53+
}];
54+
string name = 2 [(validate.rules).string.pattern = "^[\\w|-]+$"]; // Single word optionally separated with _ or -
55+
bool optional = 3;
56+
// If a material is set as output it will get added to the subject in the statement
57+
bool output = 4;
58+
// List of annotations that can be used to add metadata to the material
59+
// this metadata can be used later on by the integrations engine to filter and interpolate data
60+
repeated Annotation annotations = 5;
5761

58-
enum MaterialType {
59-
MATERIAL_TYPE_UNSPECIFIED = 0;
60-
STRING = 1;
61-
CONTAINER_IMAGE = 2;
62-
ARTIFACT = 3;
63-
SBOM_CYCLONEDX_JSON = 4;
64-
SBOM_SPDX_JSON = 5;
65-
JUNIT_XML = 6;
66-
// SARIF = 5;
67-
}
62+
enum MaterialType {
63+
MATERIAL_TYPE_UNSPECIFIED = 0;
64+
STRING = 1;
65+
CONTAINER_IMAGE = 2;
66+
ARTIFACT = 3;
67+
SBOM_CYCLONEDX_JSON = 4;
68+
SBOM_SPDX_JSON = 5;
69+
JUNIT_XML = 6;
70+
// https://github.com/openvex/spec
71+
OPENVEX = 7;
72+
// https://docs.oasis-open.org/csaf/csaf/v2.0/cs03/csaf-v2.0-cs03.html
73+
CSAF_VEX = 8;
6874
}
75+
}
6976
}
7077

7178
message Annotation {
72-
string name = 1 [(validate.rules).string.pattern = "^[\\w]+$"]; // Single word optionally separated with _
73-
// This value can be set in the contract or provided during the attestation
74-
string value = 2;
75-
}
79+
string name = 1 [(validate.rules).string.pattern = "^[\\w]+$"]; // Single word optionally separated with _
80+
// This value can be set in the contract or provided during the attestation
81+
string value = 2;
82+
}

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ require (
6969
github.com/invopop/jsonschema v0.7.0
7070
github.com/jackc/pgx/v5 v5.4.3
7171
github.com/muesli/reflow v0.3.0
72+
github.com/openvex/go-vex v0.2.5
7273
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
7374
google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d
7475
google.golang.org/genproto/googleapis/bytestream v0.0.0-20230822172742-b8732ec3820d
@@ -91,6 +92,7 @@ require (
9192
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
9293
github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 // indirect
9394
github.com/oklog/run v1.1.0 // indirect
95+
github.com/package-url/packageurl-go v0.1.1 // indirect
9496
github.com/pkg/xattr v0.4.9 // indirect
9597
go.opentelemetry.io/otel/metric v1.17.0 // indirect
9698
gopkg.in/go-jose/go-jose.v2 v2.6.1 // indirect

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -951,10 +951,14 @@ github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFSt
951951
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
952952
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
953953
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
954+
github.com/openvex/go-vex v0.2.5 h1:41utdp2rHgAGCsG+UbjmfMG5CWQxs15nGqir1eRgSrQ=
955+
github.com/openvex/go-vex v0.2.5/go.mod h1:j+oadBxSUELkrKh4NfNb+BPo77U3q7gdKME88IO/0Wo=
954956
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
955957
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
956958
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
957959
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
960+
github.com/package-url/packageurl-go v0.1.1 h1:KTRE0bK3sKbFKAk3yy63DpeskU7Cvs/x/Da5l+RtzyU=
961+
github.com/package-url/packageurl-go v0.1.1/go.mod h1:uQd4a7Rh3ZsVg5j0lNyAfyxIeGde9yrlhjF78GzeW0c=
958962
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
959963
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
960964
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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 materials
17+
18+
import (
19+
"context"
20+
"fmt"
21+
22+
api "github.com/chainloop-dev/chainloop/app/cli/api/attestation/v1"
23+
schemaapi "github.com/chainloop-dev/chainloop/app/controlplane/api/workflowcontract/v1"
24+
"github.com/chainloop-dev/chainloop/internal/casclient"
25+
"github.com/openvex/go-vex/pkg/csaf"
26+
"github.com/rs/zerolog"
27+
)
28+
29+
type CSAFVEXCrafter struct {
30+
backend *casclient.CASBackend
31+
*crafterCommon
32+
}
33+
34+
func NewCSAFVEXCrafter(materialSchema *schemaapi.CraftingSchema_Material, backend *casclient.CASBackend, l *zerolog.Logger) (*CSAFVEXCrafter, error) {
35+
if materialSchema.Type != schemaapi.CraftingSchema_Material_CSAF_VEX {
36+
return nil, fmt.Errorf("material type is not CSAF_VEX format")
37+
}
38+
39+
return &CSAFVEXCrafter{
40+
backend: backend,
41+
crafterCommon: &crafterCommon{logger: l, input: materialSchema},
42+
}, nil
43+
}
44+
45+
func (i *CSAFVEXCrafter) Craft(ctx context.Context, filepath string) (*api.Attestation_Material, error) {
46+
i.logger.Debug().Str("path", filepath).Msg("decoding CSAF VEX file")
47+
doc, err := csaf.Open(filepath)
48+
// parse doesn't fail if the provided file is a valid JSON, but not a valid CSAF VEX file
49+
if err != nil || doc.Document.Title == "" {
50+
if err != nil {
51+
i.logger.Debug().Err(err).Msg("error decoding file")
52+
}
53+
54+
return nil, fmt.Errorf("invalid CSAF VEX file: %w", ErrInvalidMaterialType)
55+
}
56+
57+
return uploadAndCraft(ctx, i.input, i.backend, filepath, i.logger)
58+
}

0 commit comments

Comments
 (0)