Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add SLSA tag in summary report when available #301

Merged
merged 1 commit into from
Dec 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions pkg/models/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,22 @@ func GetModelEcosystem(ecosystem packagev1.Ecosystem) string {
}
}

type ProvenanceType string

const (
ProvenanceTypeSlsa = ProvenanceType("slsa")
)

// Represents an abstract provenance of a package provided
// by different sources such as deps.dev or other sources
type Provenance struct {
Type ProvenanceType `json:"type"`
CommitSHA string `json:"commit_sha"`
SourceRepository string `json:"source_url"`
Url string `json:"url"`
Verified bool `json:"verified"`
}

// Represents a package such as a version of a library defined as a dependency
// in Gemfile.lock, pom.xml etc.
type Package struct {
Expand All @@ -317,6 +333,9 @@ type Package struct {
// Depth of this package in dependency tree
Depth int `json:"depth"`

// Optional provenances for this package
Provenances []*Provenance `json:"provenances"`

// Manifest from where this package was found directly or indirectly
Manifest *PackageManifest `json:"-"`
}
Expand Down Expand Up @@ -349,6 +368,10 @@ func (p *Package) GetVersion() string {
return p.Version
}

func (p *Package) GetProvenances() []*Provenance {
return p.Provenances
}

func (p *Package) ShortName() string {
return fmt.Sprintf("pkg:%s/%s@%s",
strings.ToLower(string(p.Ecosystem)),
Expand Down
60 changes: 58 additions & 2 deletions pkg/reporter/summary.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ type summaryReporter struct {
unpopular int
drifts int
}

provenance struct {
none int
verified int
unverified int
}
}

// Map of pkgId and associated meta for building remediation advice
Expand Down Expand Up @@ -122,6 +128,7 @@ func (r *summaryReporter) AddManifest(manifest *models.PackageManifest) {
r.processForMalware(pkg)
r.processForPopularity(pkg)
r.processForVersionDrift(pkg)
r.processForProvenance(pkg)

r.summary.packages += 1
return nil
Expand Down Expand Up @@ -225,6 +232,27 @@ func (r *summaryReporter) processForMalware(pkg *models.Package) {
}
}

func (r *summaryReporter) processForProvenance(pkg *models.Package) {
if len(pkg.GetProvenances()) == 0 {
r.summary.provenance.none += 1
return
}

verified := false
for _, p := range pkg.GetProvenances() {
if p.Verified {
verified = true
break
}
}

if verified {
r.summary.provenance.verified += 1
} else {
r.summary.provenance.unverified += 1
}
}

func (r *summaryReporter) processForVulns(pkg *models.Package) {
insight := utils.SafelyGetValue(pkg.Insights)
vulns := utils.SafelyGetValue(insight.Vulnerabilities)
Expand Down Expand Up @@ -304,6 +332,8 @@ func (r *summaryReporter) Finish() error {
fmt.Println()
fmt.Println(text.FgHiYellow.Sprint(summaryListPrependText, r.popularityCountStatement()))
fmt.Println()
fmt.Println(text.FgHiYellow.Sprint(summaryListPrependText, r.provenanceStatement()))
fmt.Println()
fmt.Println(text.FgHiYellow.Sprint(summaryListPrependText, r.majorVersionDriftStatement()))
fmt.Println()
fmt.Println(text.Faint.Sprint(summaryListPrependText, r.manifestCountStatement()))
Expand Down Expand Up @@ -421,7 +451,7 @@ func (r *summaryReporter) renderRemediationAdvice() {
return
}

fmt.Println(text.Bold.Sprint("Consider upgrading the following libraries for maximum impact:"))
fmt.Println(text.Bold.Sprint(fmt.Sprintf("Top %d libraries to upgrade ...", r.config.MaxAdvice)))
fmt.Println()

tbl := table.NewWriter()
Expand Down Expand Up @@ -472,7 +502,7 @@ func (r *summaryReporter) addRemediationAdviceTableRows(tbl table.Writer,
// Add the package as a table row
tbl.AppendRow(table.Row{
string(sp.pkg.Ecosystem),
r.packageNameForRemediationAdvice(sp.pkg),
r.packageNameForRemediationAdvice(sp.pkg) + " " + r.slsaTagFor(sp.pkg),
r.packageUpdateVersionForRemediationAdvice(sp.pkg),
sp.score,
r.packageVulnerabilityRiskText(sp.pkg),
Expand Down Expand Up @@ -607,6 +637,26 @@ func (r *summaryReporter) packageNameForRemediationAdvice(pkg *models.Package) s
pkg.PackageDetails.Version)
}

func (r *summaryReporter) slsaTagFor(pkg *models.Package) string {
var slsaProvenance *models.Provenance
for _, p := range pkg.GetProvenances() {
if p.Type == models.ProvenanceTypeSlsa {
slsaProvenance = p
break
}
}

if slsaProvenance != nil {
if slsaProvenance.Verified {
return text.BgGreen.Sprint(" slsa: verified ")
} else {
return text.BgRed.Sprint(" slsa: unverified ")
}
}

return ""
}

func (r *summaryReporter) packageUpdateVersionForRemediationAdvice(pkg *models.Package) string {
insight := utils.SafelyGetValue(pkg.Insights)
insightsCurrentVersion := utils.SafelyGetValue(insight.PackageCurrentVersion)
Expand Down Expand Up @@ -640,6 +690,12 @@ func (r *summaryReporter) popularityCountStatement() string {
r.summary.metrics.unpopular)
}

func (r *summaryReporter) provenanceStatement() string {
return fmt.Sprintf("Provenance: %d verified, %d unverified, %d missing",
r.summary.provenance.verified, r.summary.provenance.unverified,
r.summary.provenance.none)
}

func (r *summaryReporter) majorVersionDriftStatement() string {
return fmt.Sprintf("%d libraries are out of date with major version drift in direct dependencies",
r.summary.metrics.drifts)
Expand Down
24 changes: 24 additions & 0 deletions pkg/scanner/enrich_insightsv2.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,35 @@ func (e *insightsBasedPackageEnricherV2) applyInsights(pkg *models.Package,
// Apply the V1 insights to the package
pkg.Insights = insightsv1

// Apply provenance if available
pkg.Provenances = e.getProvenances(res)

// Finally, store the new insights model :)
pkg.InsightsV2 = res.GetInsight()
return nil
}

func (e *insightsBasedPackageEnricherV2) getProvenances(res *insightsv2.GetPackageVersionInsightResponse) []*models.Provenance {
pkgProvenances := []*models.Provenance{}

provenances := res.GetInsight().GetSlsaProvenances()
if len(provenances) == 0 {
return pkgProvenances
}

for _, p := range provenances {
pkgProvenances = append(pkgProvenances, &models.Provenance{
Type: models.ProvenanceTypeSlsa,
SourceRepository: p.GetSourceRepository(),
CommitSHA: p.GetCommitSha(),
Url: p.GetUrl(),
Verified: p.GetVerified(),
})
}

return pkgProvenances
}

func (e *insightsBasedPackageEnricherV2) convertInsightsV2ToV1(pvi *packagev1.PackageVersionInsight) (*insightapi.PackageVersionInsight, error) {
insights := &insightapi.PackageVersionInsight{}

Expand Down
1 change: 1 addition & 0 deletions test/scenarios/all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ bash $E2E_THIS_DIR/scenario-4-lfp-fail-fast.sh
bash $E2E_THIS_DIR/scenario-5-gradle-depgraph-build.sh
bash $E2E_THIS_DIR/scenario-6-manifest-flag.sh
bash $E2E_THIS_DIR/scenario-7-rubygems-project-url.sh
bash $E2E_THIS_DIR/scenario-8-summary-report.sh
14 changes: 14 additions & 0 deletions test/scenarios/scenario-8-summary-report.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/bash

set -ex

if [ "$E2E_VET_INSIGHTS_V2" != "true" ]; then
echo "Skipping scenario-8-summary-report.sh as E2E_INSIGHTS_V2 is not set to true"
exit 0
fi

$(echo \
$E2E_VET_SCAN_CMD \
scan --purl pkg:/npm/@clerk/nextjs@6.9.6
) | grep "slsa: verified"

Loading