Built and signed with sigstore using GitHub Actions.
\n")
+ reportBuilder.WriteString("\n")
+ reportBuilder.WriteString("Attribute | Details |
\n")
+ reportBuilder.WriteString(fmt.Sprintf("Source repo | %s |
\n", result.Provenance.Description.Provenance.SourceRepo, 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, result.Provenance.Description.Provenance.Transparency))
} else {
// need to write regular provenance info
- reportBuilder.WriteString("
\n\n")
- reportBuilder.WriteString("| | |\n")
- reportBuilder.WriteString("| --- | --- |\n")
- reportBuilder.WriteString(fmt.Sprintf("| Number of versions | %.0f |\n", result.Provenance.Description.Hp.Versions))
- reportBuilder.WriteString(fmt.Sprintf("| Number of Git Tags/Releases | %.0f |\n", result.Provenance.Description.Hp.Tags))
- reportBuilder.WriteString(fmt.Sprintf("| Number of versions matched to Git Tags/Releases | %.0f |\n", result.Provenance.Description.Hp.Common))
+ if result.Provenance.Description.Hp.Common > 2 {
+ reportBuilder.WriteString("Proof of origin (Git Tags) ✅\n\n\n")
+ reportBuilder.WriteString("This package can be mapped to the source code repository, based on the density of Git tags/releases.
\n")
+ } else {
+ reportBuilder.WriteString("Proof of origin (Git Tags) ❌ (failed)\n\n")
+ reportBuilder.WriteString("This package could not be mapped to the source code repository based on the density of Git tags/releases.
\n")
+ }
+
+ reportBuilder.WriteString("\n")
+ reportBuilder.WriteString("Attribute | Count |
\n")
+ reportBuilder.WriteString(fmt.Sprintf("Number of versions | %.0f |
\n", result.Provenance.Description.Hp.Versions))
+ reportBuilder.WriteString(fmt.Sprintf("Number of Git Tags/Releases | %.0f |
\n", result.Provenance.Description.Hp.Tags))
+ reportBuilder.WriteString(fmt.Sprintf("Number of versions matched to Git Tags/Releases | %.0f |
\n", result.Provenance.Description.Hp.Common))
}
- reportBuilder.WriteString("\n[Learn more about source of origin provenance](https://docs.stacklok.com/trusty/understand/provenance)\n") // Ensure newlines around this link
+ reportBuilder.WriteString("
\n")
+ reportBuilder.WriteString("\nLearn more about source of origin provenance
\n")
reportBuilder.WriteString("\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("\n")
- reportBuilder.WriteString("Alternative Packages 💡
\n\n")
+ reportBuilder.WriteString("Alternative Package Recommendations 💡
\n\n")
reportBuilder.WriteString("| Package | Score | Trusty Link |\n")
reportBuilder.WriteString("| ------- | ----- | ---------- |\n")
for _, alt := range result.Alternatives.Packages {
@@ -241,7 +281,7 @@ func ProcessDependency(dep string, ecosystem string, globalThreshold float64, re
shouldFail = true
}
- return reportBuilder.String(), shouldFail, details
+ return reportBuilder.String(), shouldFail
}
// fetchPackageData fetches package data from the specified request URL for a given dependency and ecosystem.
@@ -303,51 +343,3 @@ func fetchPackageData(requestURL, dep, ecosystem string, resultChan chan<- Packa
}
}()
}
-
-// BuildReport analyzes the dependencies of a PR and generates a report based on their Trusty scores.
-// It takes the following parameters:
-// - ctx: The context.Context for the function.
-// - ghClient: A pointer to a github.Client for interacting with the GitHub API.
-// - owner: The owner of the repository.
-// - repo: The name of the repository.
-// - prNumber: The number of the pull request.
-// - dependencies: A slice of strings representing the dependencies to be analyzed.
-// - ecosystem: The ecosystem of the dependencies (e.g., "npm", "pip", "maven").
-// - scoreThreshold: The threshold for Trusty scores below which a warning will be generated.
-//
-// The function generates a report and posts it as a comment on the pull request.
-func BuildReport(ctx context.Context,
- ghClient *github.Client,
- owner,
- repo string,
- prNumber int,
- dependencies []string,
- ecosystem string,
- globalThreshold float64,
- repoActivityThreshold float64,
- authorActivityThreshold float64,
- provenanceThreshold float64,
- typosquattingThreshold float64,
- failOnMalicious bool,
- failOnDeprecated bool,
- failOnArchived bool) {
-
- reportContent, failAction := GenerateReportContent(dependencies, ecosystem, globalThreshold, repoActivityThreshold, authorActivityThreshold, provenanceThreshold, typosquattingThreshold,
- failOnMalicious, failOnDeprecated, failOnArchived)
-
- if strings.TrimSpace(reportContent) != "## 🐻 Trusty Dependency Analysis Action Report \n\n" {
- _, _, err := ghClient.Issues.CreateComment(ctx, owner, repo, prNumber, &github.IssueComment{Body: &reportContent})
- if err != nil {
- log.Printf("error posting comment to PR: %v\n", err)
- } else {
- log.Printf("posted comment to PR: %s/%s#%d\n", owner, repo, prNumber)
- }
- } else {
- log.Println("No report content to post, skipping comment.")
- }
-
- if failAction {
- log.Println("Failing the GitHub Action due to dependencies not meeting the required criteria.")
- os.Exit(1)
- }
-}
diff --git a/pkg/trustyapi/trustyapi_test.go b/pkg/trustyapi/trustyapi_test.go
index a576964..359e5c1 100644
--- a/pkg/trustyapi/trustyapi_test.go
+++ b/pkg/trustyapi/trustyapi_test.go
@@ -35,13 +35,10 @@ func TestProcessGoDependencies(t *testing.T) {
for i, dep := range dependencies {
log.Printf("Analyzing dependency: %s\n", dep)
- report, shouldFail, dependencyDetails := ProcessDependency(dep, ecosystem, repoActivityThreshold, authorActivityThreshold, provenanceThreshold, typosquattingThreshold, scoreThreshold, true, true, true)
+ report, shouldFail := ProcessDependency(dep, ecosystem, repoActivityThreshold, authorActivityThreshold, provenanceThreshold, typosquattingThreshold, scoreThreshold, true, true, true)
if shouldFail != expectedFail[i] {
t.Errorf("Dependency %s failed check unexpectedly, expected %v, got %v", dep, expectedFail[i], shouldFail)
}
- if dependencyDetails.Name != dep {
- t.Errorf("Dependency name mismatch, expected %s, got %s", dep, dependencyDetails.Name)
- }
if dep == "github.com/Tinkoff/libvirt-exporter" {
if !strings.Contains(report, "Archived") {
t.Errorf("Expected report to contain 'Archived' for %s", dep)
@@ -60,7 +57,7 @@ func TestProcessDeprecatedDependencies(t *testing.T) {
for _, dep := range dependencies {
log.Printf("Analyzing dependency: %s\n", dep)
- report, _, _ := ProcessDependency(dep, ecosystem, scoreThreshold, 0.0, 0.0, 0.0, 0.0, true, true, true)
+ report, _ := ProcessDependency(dep, ecosystem, scoreThreshold, 0.0, 0.0, 0.0, 0.0, true, true, true)
if !strings.Contains(report, "Deprecated") {
t.Errorf("Expected report to contain 'Deprecated' for %s", dep)
}
@@ -76,7 +73,7 @@ func TestProcessMaliciousDependencies(t *testing.T) {
for _, dep := range dependencies {
log.Printf("Analyzing dependency: %s\n", dep)
- report, _, _ := ProcessDependency(dep, ecosystem, scoreThreshold, 0.0, 0.0, 0.0, 0.0, true, true, true)
+ report, _ := ProcessDependency(dep, ecosystem, scoreThreshold, 0.0, 0.0, 0.0, 0.0, true, true, true)
if !strings.Contains(report, "Malicious") {
t.Errorf("Expected report to contain 'Malicious' for %s", dep)
}
@@ -88,7 +85,7 @@ func TestProcessSigstoreProvenance(t *testing.T) {
ecosystem := "npm"
scoreThreshold := 5.0
- report, _, _ := ProcessDependency("sigstore", ecosystem, scoreThreshold, 0.0, 0.0, 0.0, 0.0, true, true, true)
+ report, _ := ProcessDependency("sigstore", ecosystem, scoreThreshold, 0.0, 0.0, 0.0, 0.0, true, true, true)
if !strings.Contains(report, "sigstore") {
t.Errorf("Expected report to contain 'sigstore'")
}
@@ -107,7 +104,7 @@ func TestProcessHistoricalProvenance(t *testing.T) {
ecosystem := "npm"
scoreThreshold := 5.0
- report, _, _ := ProcessDependency("openpgp", ecosystem, scoreThreshold, 0.0, 0.0, 0.0, 0.0, true, true, true)
+ report, _ := ProcessDependency("openpgp", ecosystem, scoreThreshold, 0.0, 0.0, 0.0, 0.0, true, true, true)
if !strings.Contains(report, "Number of versions") {
t.Errorf("Versions for historical provenance not populated")
}