Skip to content

Commit e1b9dc5

Browse files
authored
feat(materials): New JACOCO_XML material type and support for policies (chainloop-dev#1624)
Signed-off-by: Jose I. Paris <[email protected]>
1 parent 92ebce3 commit e1b9dc5

25 files changed

+366
-36
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ Chainloop supports the collection of the following list of evidence types. For t
140140
- [CSAF VEX](https://docs.oasis-open.org/csaf/csaf/v2.0/os/csaf-v2.0-os.html#45-profile-5-vex)
141141
- [Gitlab Security report](https://docs.gitlab.com/ee/user/application_security/)
142142
- [JUnit](https://www.ibm.com/docs/en/developer-for-zos/14.1?topic=formats-junit-xml-format)
143+
- [JaCoCo XML Coverage Reports](https://www.jacoco.org/jacoco/trunk/doc/)
143144
- Attestation: existing Chainloop attestations.
144145
- Artifact Type: It represents a software artifact.
145146
- Custom Evidence Type: Custom piece of evidence that doesn't fit in any other category, for instance, an approval report in json format, etc.

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

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

app/controlplane/api/gen/jsonschema/attestation.v1.Attestation.Material.jsonschema.json

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

app/controlplane/api/gen/jsonschema/attestation.v1.Attestation.Material.schema.json

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

app/controlplane/api/gen/jsonschema/attestation.v1.PolicyEvaluation.jsonschema.json

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

app/controlplane/api/gen/jsonschema/attestation.v1.PolicyEvaluation.schema.json

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

app/controlplane/api/gen/jsonschema/workflowcontract.v1.CraftingSchema.Material.jsonschema.json

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

app/controlplane/api/gen/jsonschema/workflowcontract.v1.CraftingSchema.Material.schema.json

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

app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicyGroup.Material.jsonschema.json

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

app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicyGroup.Material.schema.json

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

app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpec.jsonschema.json

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

app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpec.schema.json

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

app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpecV2.jsonschema.json

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

app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpecV2.schema.json

Lines changed: 2 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: 9 additions & 4 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: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ message CraftingSchema {
121121
GHAS_SECRET_SCAN = 21;
122122
// https://docs.github.com/en/rest/dependabot/alerts?apiVersion=2022-11-28
123123
GHAS_DEPENDENCY_SCAN = 22;
124+
// Jacoco coverage reports https://www.jacoco.org/userdoc/importexport.html
125+
JACOCO_XML = 23;
124126
}
125127
}
126128
}

app/controlplane/api/workflowcontract/v1/crafting_schema_validations.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ var CraftingMaterialInValidationOrder = []CraftingSchema_Material_MaterialType{
3939
CraftingSchema_Material_CSAF_SECURITY_INCIDENT_RESPONSE,
4040
CraftingSchema_Material_GITLAB_SECURITY_REPORT,
4141
CraftingSchema_Material_JUNIT_XML,
42+
CraftingSchema_Material_JACOCO_XML,
4243
CraftingSchema_Material_HELM_CHART,
4344
CraftingSchema_Material_SARIF,
4445
CraftingSchema_Material_BLACKDUCK_SCA_JSON,

docs/docs/partials/materials.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@
3333
"link": "https://www.ibm.com/docs/en/developer-for-zos/14.1?topic=formats-junit-xml-format",
3434
"id": "JUNIT_XML"
3535
},
36+
{
37+
"name": "JaCoCo XML Report",
38+
"link": "https://www.jacoco.org/jacoco/trunk/doc/",
39+
"id": "JACOCO_XML"
40+
},
3641
{
3742
"name": "Helm Chart",
3843
"link": "https://helm.sh/docs/topics/charts/",

pkg/attestation/crafter/api/attestation/v1/crafting_state.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,16 @@ package v1
1818
import (
1919
"bytes"
2020
"encoding/json"
21+
"encoding/xml"
2122
"errors"
2223
"fmt"
2324
"os"
2425
"strings"
2526

2627
v1 "github.com/chainloop-dev/chainloop/app/controlplane/api/workflowcontract/v1"
28+
"github.com/chainloop-dev/chainloop/pkg/attestation/crafter/materials/jacoco"
2729
intoto "github.com/in-toto/attestation/go/v1"
30+
"github.com/joshdk/go-junit"
2831
"github.com/secure-systems-lab/go-securesystemslib/dsse"
2932
"google.golang.org/protobuf/types/known/structpb"
3033
)
@@ -104,6 +107,29 @@ func (m *Attestation_Material) GetEvaluableContent(value string) ([]byte, error)
104107
}
105108
}
106109

110+
// For XML based materials, we need to ingest them and read as json-like structure
111+
switch m.MaterialType {
112+
case v1.CraftingSchema_Material_JUNIT_XML:
113+
suites, err := junit.Ingest(rawMaterial)
114+
if err != nil {
115+
return nil, fmt.Errorf("failed to ingest junit xml: %w", err)
116+
}
117+
// this will render a json array
118+
rawMaterial, err = json.Marshal(suites)
119+
if err != nil {
120+
return nil, fmt.Errorf("failed to marshal junit xml: %w", err)
121+
}
122+
case v1.CraftingSchema_Material_JACOCO_XML:
123+
var report jacoco.Report
124+
if err := xml.Unmarshal(rawMaterial, &report); err != nil {
125+
return nil, fmt.Errorf("invalid Jacoco report file: %w", err)
126+
}
127+
rawMaterial, err = json.Marshal(&report)
128+
if err != nil {
129+
return nil, fmt.Errorf("failed to marshal to json Jacoco report file: %w", err)
130+
}
131+
}
132+
107133
// if raw material is empty (container images, for example), let's create an empty json
108134
if len(rawMaterial) == 0 {
109135
rawMaterial = []byte(`{}`)
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//
2+
// Copyright 2024 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+
"encoding/xml"
21+
"fmt"
22+
"io"
23+
"os"
24+
"slices"
25+
26+
schemaapi "github.com/chainloop-dev/chainloop/app/controlplane/api/workflowcontract/v1"
27+
"github.com/chainloop-dev/chainloop/internal/casclient"
28+
api "github.com/chainloop-dev/chainloop/pkg/attestation/crafter/api/attestation/v1"
29+
"github.com/chainloop-dev/chainloop/pkg/attestation/crafter/materials/jacoco"
30+
"github.com/rs/zerolog"
31+
)
32+
33+
type JacocoCrafter struct {
34+
*crafterCommon
35+
backend *casclient.CASBackend
36+
}
37+
38+
func NewJacocoCrafter(schema *schemaapi.CraftingSchema_Material, backend *casclient.CASBackend, l *zerolog.Logger) *JacocoCrafter {
39+
return &JacocoCrafter{
40+
crafterCommon: &crafterCommon{logger: l, input: schema},
41+
backend: backend,
42+
}
43+
}
44+
45+
func (c *JacocoCrafter) Craft(ctx context.Context, filePath string) (*api.Attestation_Material, error) {
46+
f, err := os.Open(filePath)
47+
if err != nil {
48+
return nil, fmt.Errorf("can't open the file: %w", err)
49+
}
50+
defer f.Close()
51+
52+
bytes, err := io.ReadAll(f)
53+
if err != nil {
54+
return nil, fmt.Errorf("can't read the file: %w", err)
55+
}
56+
57+
var report jacoco.Report
58+
59+
if err := xml.Unmarshal(bytes, &report); err != nil {
60+
return nil, fmt.Errorf("invalid Jacoco report file: %w", ErrInvalidMaterialType)
61+
}
62+
63+
if len(report.Counters) == 0 {
64+
return nil, fmt.Errorf("invalid Jacoco report file, no counters found: %w", ErrInvalidMaterialType)
65+
}
66+
// At least "instruction" counter should be available according to the documentation
67+
// https://www.eclemma.org/jacoco/trunk/doc/counters.html
68+
if !slices.ContainsFunc(report.Counters, func(counter *jacoco.Counter) bool {
69+
return counter.Type == "INSTRUCTION"
70+
}) {
71+
return nil, fmt.Errorf("invalid Jacoco report file: %w", ErrInvalidMaterialType)
72+
}
73+
return uploadAndCraft(ctx, c.input, c.backend, filePath, c.logger)
74+
}

0 commit comments

Comments
 (0)