Skip to content

Commit

Permalink
Only store attestation data that is needed
Browse files Browse the repository at this point in the history
The whole attestation is being stored for each
component that is evaluated, but in most cases
only a subset of the attestation printed.
This change captures only the needed attestation
data based on the output selected at runtime.

https://issues.redhat.com/browse/EC-1026
  • Loading branch information
joejstuart committed Dec 8, 2024
1 parent 27b87f9 commit 8387eab
Show file tree
Hide file tree
Showing 8 changed files with 67 additions and 81 deletions.
15 changes: 11 additions & 4 deletions cmd/validate/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -364,10 +364,17 @@ func validateImageCmd(validate imageValidationFunc) *cobra.Command {
}

res.component.Signatures = out.Signatures
res.component.Attestations = out.Attestations
// Create a new result object for attestations. The point is to only keep the data that's needed.
// For example, the Statement is only needed when the full attestation is printed.
for _, att := range out.Attestations {
attResult := applicationsnapshot.NewAttestationResult(att)
if containsOutput(data.output, "attestation") {
attResult.Statement = att.Statement()
}
res.component.Attestations = append(res.component.Attestations, attResult)

Check warning on line 374 in cmd/validate/image.go

View check run for this annotation

Codecov / codecov/patch

cmd/validate/image.go#L370-L374

Added lines #L370 - L374 were not covered by tests
}
res.component.ContainerImage = out.ImageURL
res.data = out.Data
res.component.Attestations = out.Attestations
res.policyInput = out.PolicyInput
}
res.component.Success = err == nil && len(res.component.Violations) == 0
Expand Down Expand Up @@ -411,7 +418,7 @@ func validateImageCmd(validate imageValidationFunc) *cobra.Command {
} else {
components = append(components, r.component)
// evaluator data is duplicated per component, so only collect it once.
if len(evaluatorData) == 0 && containsData(data.output) {
if len(evaluatorData) == 0 && containsOutput(data.output, "data") {
evaluatorData = append(evaluatorData, r.data)
}
manyPolicyInput = append(manyPolicyInput, r.policyInput)
Expand Down Expand Up @@ -543,7 +550,7 @@ func validateImageCmd(validate imageValidationFunc) *cobra.Command {
}

// find if the slice contains "data" output
func containsData(data []string) bool {
func containsOutput(data []string, value string) bool {
for _, item := range data {
newItem := strings.Split(item, "=")
if newItem[0] == "data" {
Expand Down
2 changes: 1 addition & 1 deletion cmd/validate/image_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1322,7 +1322,7 @@ func TestContainsData(t *testing.T) {
}

for _, test := range tests {
result := containsData(test.input)
result := containsOutput(test.input, "data")
assert.Equal(t, test.expected, result, test.name)
}
}
2 changes: 1 addition & 1 deletion cmd/validate/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ func validateInputCmd(validate InputValidationFunc) *cobra.Command {
} else {
inputs = append(inputs, r.input)
// evaluator data is duplicated per component, so only collect it once.
if len(evaluatorData) == 0 && containsData(data.output) {
if len(evaluatorData) == 0 && containsOutput(data.output, "data") {

Check warning on line 180 in cmd/validate/input.go

View check run for this annotation

Codecov / codecov/patch

cmd/validate/input.go#L180

Added line #L180 was not covered by tests
evaluatorData = append(evaluatorData, r.data)
}
manyPolicyInput = append(manyPolicyInput, r.policyInput)
Expand Down
33 changes: 31 additions & 2 deletions internal/applicationsnapshot/attestation.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,43 @@ import (
"encoding/json"

"github.com/in-toto/in-toto-golang/in_toto"

"github.com/enterprise-contract/ec-cli/internal/attestation"
"github.com/enterprise-contract/ec-cli/internal/signature"
)

type SLSAProvenance interface {
attestation.Attestation
PredicateBuildType() string
}

type AttestationResult struct {
Type string `json:"type"`
PredicateType string `json:"predicateType"`
PredicateBuildType string `json:"predicateBuildType"`
Signatures []signature.EntitySignature `json:"signatures"`
Statement []byte `json:"statement"`
}

func NewAttestationResult(att attestation.Attestation) AttestationResult {
attResult := AttestationResult{
Type: att.Type(),
PredicateType: att.PredicateType(),
Signatures: att.Signatures(),
}
if value, ok := att.(SLSAProvenance); ok {
attResult.PredicateBuildType = value.PredicateBuildType()

}
return attResult

Check warning on line 52 in internal/applicationsnapshot/attestation.go

View check run for this annotation

Codecov / codecov/patch

internal/applicationsnapshot/attestation.go#L42-L52

Added lines #L42 - L52 were not covered by tests
}

func (r *Report) renderAttestations() ([]byte, error) {
byts := make([][]byte, 0, len(r.Components)*2)

for _, c := range r.Components {
for _, a := range c.Attestations {
byts = append(byts, a.Statement())
byts = append(byts, a.Statement)
}
}

Expand All @@ -40,7 +69,7 @@ func (r *Report) attestations() ([]in_toto.Statement, error) {
for _, c := range r.Components {
for _, a := range c.Attestations {
var statement in_toto.Statement
err := json.Unmarshal(a.Statement(), &statement)
err := json.Unmarshal(a.Statement, &statement)
if err != nil {
return []in_toto.Statement{}, nil
}
Expand Down
48 changes: 11 additions & 37 deletions internal/applicationsnapshot/attestation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@ import (
app "github.com/konflux-ci/application-api/api/v1alpha1"
"github.com/stretchr/testify/assert"

"github.com/enterprise-contract/ec-cli/internal/attestation"
"github.com/enterprise-contract/ec-cli/internal/evaluator"
"github.com/enterprise-contract/ec-cli/internal/signature"
)

func TestAttestationReport(t *testing.T) {
Expand All @@ -55,7 +53,7 @@ func TestAttestationReport(t *testing.T) {
SnapshotComponent: app.SnapshotComponent{
ContainerImage: "registry.io/repository/image:tag",
},
Attestations: []attestation.Attestation{
Attestations: []AttestationResult{
att("attestation1"),
},
},
Expand All @@ -68,7 +66,7 @@ func TestAttestationReport(t *testing.T) {
SnapshotComponent: app.SnapshotComponent{
ContainerImage: "registry.io/repository/image1:tag",
},
Attestations: []attestation.Attestation{
Attestations: []AttestationResult{
att("attestation1"),
att("attestation2"),
},
Expand All @@ -77,7 +75,7 @@ func TestAttestationReport(t *testing.T) {
SnapshotComponent: app.SnapshotComponent{
ContainerImage: "registry.io/repository/image2:tag",
},
Attestations: []attestation.Attestation{
Attestations: []AttestationResult{
att("attestation3"),
att("attestation4"),
},
Expand All @@ -91,7 +89,7 @@ func TestAttestationReport(t *testing.T) {
SnapshotComponent: app.SnapshotComponent{
ContainerImage: "registry.io/repository/image1:tag",
},
Attestations: []attestation.Attestation{
Attestations: []AttestationResult{
att("attestation1"),
},
},
Expand All @@ -104,7 +102,7 @@ func TestAttestationReport(t *testing.T) {
SnapshotComponent: app.SnapshotComponent{
ContainerImage: "registry.io/repository/image3:tag",
},
Attestations: []attestation.Attestation{
Attestations: []AttestationResult{
att("attestation2"),
att("attestation3"),
},
Expand Down Expand Up @@ -145,9 +143,9 @@ func TestAttestations(t *testing.T) {
Message: "violation1",
},
},
Attestations: []attestation.Attestation{
provenance{
data: data,
Attestations: []AttestationResult{
{
Statement: data,
},
},
},
Expand All @@ -159,32 +157,8 @@ func TestAttestations(t *testing.T) {
assert.Equal(t, []in_toto.Statement{statement}, att)
}

type mockAttestation struct {
data string
}

func (a mockAttestation) Type() string {
return "type"
}

func (a mockAttestation) PredicateType() string {
return "predicateType"
}

func (a mockAttestation) Statement() []byte {
return []byte(a.data)
}

func (a mockAttestation) Signatures() []signature.EntitySignature {
return nil
}

func (a mockAttestation) Subject() []in_toto.Subject {
return []in_toto.Subject{}
}

func att(data string) attestation.Attestation {
return &mockAttestation{
data: data,
func att(data string) AttestationResult {
return AttestationResult{
Statement: []byte(data),
}
}
3 changes: 1 addition & 2 deletions internal/applicationsnapshot/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import (
app "github.com/konflux-ci/application-api/api/v1alpha1"
"sigs.k8s.io/yaml"

"github.com/enterprise-contract/ec-cli/internal/attestation"
"github.com/enterprise-contract/ec-cli/internal/evaluator"
"github.com/enterprise-contract/ec-cli/internal/format"
"github.com/enterprise-contract/ec-cli/internal/policy"
Expand All @@ -46,7 +45,7 @@ type Component struct {
Success bool `json:"success"`
SuccessCount int `json:"-"`
Signatures []signature.EntitySignature `json:"signatures,omitempty"`
Attestations []attestation.Attestation `json:"attestations,omitempty"`
Attestations []AttestationResult `json:"attestations,omitempty"`
}

type Report struct {
Expand Down
41 changes: 7 additions & 34 deletions internal/applicationsnapshot/vsa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,38 +29,11 @@ import (
app "github.com/konflux-ci/application-api/api/v1alpha1"
"github.com/stretchr/testify/assert"

"github.com/enterprise-contract/ec-cli/internal/attestation"
"github.com/enterprise-contract/ec-cli/internal/evaluator"
"github.com/enterprise-contract/ec-cli/internal/policy"
"github.com/enterprise-contract/ec-cli/internal/signature"
"github.com/enterprise-contract/ec-cli/internal/utils"
)

type provenance struct {
statement in_toto.Statement
data []byte
}

func (p provenance) Type() string {
return in_toto.StatementInTotoV01
}

func (p provenance) PredicateType() string {
return p.statement.StatementHeader.PredicateType
}

func (p provenance) Statement() []byte {
return p.data
}

func (p provenance) Signatures() []signature.EntitySignature {
return []signature.EntitySignature{}
}

func (p provenance) Subject() []in_toto.Subject {
return p.statement.Subject
}

func TestNewVSA(t *testing.T) {
components := []Component{
{
Expand All @@ -70,9 +43,9 @@ func TestNewVSA(t *testing.T) {
Message: "violation1",
},
},
Attestations: []attestation.Attestation{
provenance{
statement: in_toto.Statement{},
Attestations: []AttestationResult{
{
Statement: []byte{},
},
},
},
Expand Down Expand Up @@ -110,6 +83,7 @@ func TestSubjects(t *testing.T) {
Digest: nil,
},
}

statement := in_toto.Statement{
StatementHeader: in_toto.StatementHeader{
Subject: expected,
Expand All @@ -126,10 +100,9 @@ func TestSubjects(t *testing.T) {
Message: "violation1",
},
},
Attestations: []attestation.Attestation{
provenance{
statement: statement,
data: data,
Attestations: []AttestationResult{
{
Statement: data,
},
},
},
Expand Down
4 changes: 4 additions & 0 deletions internal/attestation/slsa_provenance_02.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ func (a slsaProvenance) Statement() []byte {
return a.data
}

func (a slsaProvenance) PredicateBuildType() string {
return a.statement.Predicate.BuildType

Check warning on line 90 in internal/attestation/slsa_provenance_02.go

View check run for this annotation

Codecov / codecov/patch

internal/attestation/slsa_provenance_02.go#L89-L90

Added lines #L89 - L90 were not covered by tests
}

func (a slsaProvenance) Signatures() []signature.EntitySignature {
return a.signatures
}
Expand Down

0 comments on commit 8387eab

Please sign in to comment.