diff --git a/pkg/trustyapi/trustyapi.go b/pkg/trustyapi/trustyapi.go index 56d8432..2175ce2 100644 --- a/pkg/trustyapi/trustyapi.go +++ b/pkg/trustyapi/trustyapi.go @@ -144,7 +144,6 @@ func ProcessDependency(dep string, ecosystem string, scoreThreshold float64) (st log.Printf("Skipping dependency %s due to score %.2f being above the threshold %.2f\n", dep, result.Summary.Score, scoreThreshold) return "", shouldFail // shouldFail is false here, nothing to see. } - // Format the report using Markdown reportBuilder.WriteString(fmt.Sprintf("### :package: Dependency: [`%s`](https://www.trustypkg.dev/%s/%s)\n", dep, ecosystem, dep)) // Highlight if the package is malicious, deprecated or archived @@ -161,6 +160,23 @@ func ProcessDependency(dep string, ecosystem string, scoreThreshold float64) (st reportBuilder.WriteString(fmt.Sprintf("### 📉 Trusty Score: `%.2f`\n", result.Summary.Score)) + // write provenance information + if result.Provenance.Description.Provenance.Issuer != "" { + reportBuilder.WriteString("### :key: Proof of origin (Provenance):\n") + reportBuilder.WriteString("Built and signed with sigstore using GitHub Actions.\n") + reportBuilder.WriteString(fmt.Sprintf("· Source repo: `%s`\n", result.Provenance.Description.Provenance.SourceRepo)) + reportBuilder.WriteString(fmt.Sprintf("· Github Action Workflow: `%s`\n", result.Provenance.Description.Provenance.Workflow)) + reportBuilder.WriteString(fmt.Sprintf("· Issuer: `%s`\n", result.Provenance.Description.Provenance.Issuer)) + reportBuilder.WriteString(fmt.Sprintf("· Rekor Public Ledger: `%s`\n", result.Provenance.Description.Provenance.Transparency)) + } else { + // need to write regular provenance info + reportBuilder.WriteString("### :key: Proof of origin (Provenance):\n") + reportBuilder.WriteString(fmt.Sprintf("# versions: %.0f\n", result.Provenance.Description.Hp.Versions)) + reportBuilder.WriteString(fmt.Sprintf("# tags: %.0f\n", result.Provenance.Description.Hp.Tags)) + reportBuilder.WriteString(fmt.Sprintf("# matched: %.0f\n", result.Provenance.Description.Hp.Common)) + } + reportBuilder.WriteString("[Learn more about source of origin provenance](https://docs.stacklok.com/trusty/understand/provenance)\n") + // Include alternative packages in a Markdown table if available and if the package is deprecated, archived or malicious if result.Alternatives.Packages != nil && len(result.Alternatives.Packages) > 0 { reportBuilder.WriteString("### :bulb: Recommended Alternative Packages\n") diff --git a/pkg/trustyapi/trustyapi_test.go b/pkg/trustyapi/trustyapi_test.go index 7758b14..7bf9eab 100644 --- a/pkg/trustyapi/trustyapi_test.go +++ b/pkg/trustyapi/trustyapi_test.go @@ -61,3 +61,39 @@ func TestProcessMaliciousDependencies(t *testing.T) { } } + +func TestProcessSigstoreProvenance(t *testing.T) { + ecosystem := "npm" + scoreThreshold := 10.0 + + report, _ := ProcessDependency("sigstore", ecosystem, scoreThreshold) + if !strings.Contains(report, "sigstore") { + t.Errorf("Expected report to contain 'sigstore'") + } + if !strings.Contains(report, "Source repo: `https://github.com/sigstore/sigstore-js`") { + t.Errorf("Source repo not matching") + } + if !strings.Contains(report, "Github Action Workflow: `.github/workflows/release.yml`") { + t.Errorf("Github workflow not matching") + } + if !strings.Contains(report, "Issuer: `CN=sigstore-intermediate,O=sigstore.dev`") { + t.Errorf("Issuer not matching") + } +} + +func TestProcessHistoricalProvenance(t *testing.T) { + ecosystem := "npm" + scoreThreshold := 10.0 + + report, _ := ProcessDependency("openpgp", ecosystem, scoreThreshold) + if !strings.Contains(report, "# versions") { + t.Errorf("Versions for historical provenance not populated") + } + if !strings.Contains(report, "# tags") { + t.Errorf("Tags for historical provenance not populated") + } + if !strings.Contains(report, "# matched") { + t.Errorf("Matched for historical provenance not populated") + } + +}