Skip to content

Commit 45de707

Browse files
authored
Merge pull request #2657 from joejstuart/EC-1380
feat: embed per-component filtered report in VSA predicate
2 parents d40561a + ed80049 commit 45de707

2 files changed

Lines changed: 241 additions & 142 deletions

File tree

internal/validate/vsa/vsa.go

Lines changed: 67 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -22,25 +22,25 @@ import (
2222
"fmt"
2323
"os"
2424
"path/filepath"
25+
"strings"
2526
"time"
2627

28+
ecc "github.com/enterprise-contract/enterprise-contract-controller/api/v1alpha1"
2729
"github.com/sigstore/cosign/v2/pkg/oci"
2830
log "github.com/sirupsen/logrus"
2931
"github.com/spf13/afero"
3032

3133
"github.com/conforma/cli/internal/applicationsnapshot"
32-
"github.com/conforma/cli/internal/evaluator"
3334
)
3435

3536
// Predicate represents a Verification Summary Attestation (VSA) predicate.
3637
type Predicate struct {
37-
ImageRef string `json:"imageRef"`
38-
ValidationResult string `json:"validationResult"`
39-
Timestamp string `json:"timestamp"`
40-
Verifier string `json:"verifier"`
41-
PolicySource string `json:"policySource"`
42-
Component map[string]interface{} `json:"component"`
43-
RuleResults []evaluator.Result `json:"ruleResults"`
38+
ImageRef string `json:"imageRef"`
39+
Timestamp string `json:"timestamp"`
40+
Verifier string `json:"verifier"`
41+
PolicySource string `json:"policySource"`
42+
Component map[string]interface{} `json:"component"`
43+
Results *FilteredReport `json:"results,omitempty"` // Filtered report containing target component and its variants
4444
}
4545

4646
// Generator handles VSA predicate generation
@@ -57,6 +57,56 @@ func NewGenerator(report applicationsnapshot.Report, comp applicationsnapshot.Co
5757
}
5858
}
5959

60+
// FilteredReport represents a filtered version of the application snapshot report
61+
// that contains only the target component and its architecture variants.
62+
type FilteredReport struct {
63+
Snapshot string `json:"snapshot"`
64+
Components []applicationsnapshot.Component `json:"components"`
65+
Key string `json:"key"`
66+
Policy ecc.EnterpriseContractPolicySpec `json:"policy"`
67+
EcVersion string `json:"ec-version"`
68+
EffectiveTime time.Time `json:"effective-time"`
69+
}
70+
71+
// FilterReportForComponent filters the report to include only the target component and its architecture variants.
72+
// It returns a filtered report that excludes the top-level "success" field and includes only components
73+
// that match the target component name or are architecture variants of the same base component.
74+
//
75+
// Parameters:
76+
// - report: The complete application snapshot report
77+
// - targetComponentName: The name of the component to filter for
78+
//
79+
// Returns:
80+
// - A FilteredReport containing only the target component and its variants
81+
func FilterReportForComponent(report applicationsnapshot.Report, targetComponentName string) *FilteredReport {
82+
// Extract the base component name (before the first dash)
83+
baseName := targetComponentName
84+
if idx := strings.Index(targetComponentName, "-"); idx != -1 {
85+
baseName = targetComponentName[:idx]
86+
}
87+
88+
// Filter components that match the base name or are architecture variants
89+
var filteredComponents []applicationsnapshot.Component
90+
for _, comp := range report.Components {
91+
// Check if this component is the target, the base component, or a variant
92+
if comp.Name == targetComponentName ||
93+
comp.Name == baseName ||
94+
strings.HasPrefix(comp.Name, baseName+"-") {
95+
filteredComponents = append(filteredComponents, comp)
96+
}
97+
}
98+
99+
// Create a filtered report without the success field
100+
return &FilteredReport{
101+
Snapshot: report.Snapshot,
102+
Components: filteredComponents,
103+
Key: report.Key,
104+
Policy: report.Policy,
105+
EcVersion: report.EcVersion,
106+
EffectiveTime: report.EffectiveTime,
107+
}
108+
}
109+
60110
// GeneratePredicate creates a Predicate for a validated image/component.
61111
func (g *Generator) GeneratePredicate(ctx context.Context) (*Predicate, error) {
62112
log.Infof("Generating VSA predicate for image: %s", g.Component.ContainerImage)
@@ -68,30 +118,21 @@ func (g *Generator) GeneratePredicate(ctx context.Context) (*Predicate, error) {
68118
"source": g.Component.Source,
69119
}
70120

71-
// Compose rule results: combine violations, warnings, and successes
72-
ruleResults := make([]evaluator.Result, 0, len(g.Component.Violations)+len(g.Component.Warnings)+len(g.Component.Successes))
73-
ruleResults = append(ruleResults, g.Component.Violations...)
74-
ruleResults = append(ruleResults, g.Component.Warnings...)
75-
ruleResults = append(ruleResults, g.Component.Successes...)
76-
77-
validationResult := "failed"
78-
if g.Component.Success {
79-
validationResult = "passed"
80-
}
81-
82121
policySource := ""
83122
if g.Report.Policy.Name != "" {
84123
policySource = g.Report.Policy.Name
85124
}
86125

126+
// Filter the report to include only the target component and its architecture variants
127+
filteredReport := FilterReportForComponent(g.Report, g.Component.Name)
128+
87129
return &Predicate{
88-
ImageRef: g.Component.ContainerImage,
89-
ValidationResult: validationResult,
90-
Timestamp: time.Now().UTC().Format(time.RFC3339),
91-
Verifier: "ec-cli",
92-
PolicySource: policySource,
93-
Component: componentInfo,
94-
RuleResults: ruleResults,
130+
ImageRef: g.Component.ContainerImage,
131+
Timestamp: time.Now().UTC().Format(time.RFC3339),
132+
Verifier: "ec-cli",
133+
PolicySource: policySource,
134+
Component: componentInfo,
135+
Results: filteredReport,
95136
}, nil
96137
}
97138

0 commit comments

Comments
 (0)