Skip to content

Commit 50d10a0

Browse files
committed
add policy violations
1 parent 447ae30 commit 50d10a0

File tree

6 files changed

+870
-294
lines changed

6 files changed

+870
-294
lines changed

pkg/reporter/common.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
package reporter
22

3+
import (
4+
"fmt"
5+
6+
"github.com/safedep/dry/utils"
7+
"github.com/safedep/vet/gen/checks"
8+
"github.com/safedep/vet/pkg/analyzer"
9+
"github.com/safedep/vet/pkg/models"
10+
)
11+
312
type ToolMetadata struct {
413
Name string
514
Version string
@@ -8,3 +17,42 @@ type ToolMetadata struct {
817
VendorName string
918
VendorInformationURI string
1019
}
20+
21+
func getPolicyViolationSolution(event *analyzer.AnalyzerEvent) string {
22+
switch event.Filter.GetCheckType() {
23+
case checks.CheckType_CheckTypeVulnerability:
24+
return getVulnerabilitySolution(event.Package)
25+
26+
case checks.CheckType_CheckTypePopularity:
27+
return "Consider using a more popular alternative package"
28+
29+
case checks.CheckType_CheckTypeLicense:
30+
return "Review and update package to comply with license policy"
31+
32+
case checks.CheckType_CheckTypeMaintenance:
33+
return "Update package to align with maintenance policy"
34+
35+
case checks.CheckType_CheckTypeSecurityScorecard:
36+
return "Review and improve package security posture"
37+
38+
case checks.CheckType_CheckTypeMalware:
39+
return "Remove this package and review any affected code"
40+
41+
case checks.CheckType_CheckTypeOther:
42+
return "Review and fix policy violation"
43+
44+
default:
45+
return "Review and fix policy violation"
46+
}
47+
}
48+
49+
func getVulnerabilitySolution(pkg *models.Package) string {
50+
solution := "No solution available for this vulnerability"
51+
52+
if pkg.Insights != nil && pkg.Insights.PackageCurrentVersion != nil {
53+
latestVersion := utils.SafelyGetValue(pkg.Insights.PackageCurrentVersion)
54+
solution = fmt.Sprintf("Upgrade to latest version **`%s`**", latestVersion)
55+
}
56+
57+
return solution
58+
}

pkg/reporter/gitlab.go

Lines changed: 2 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import (
1515
"time"
1616

1717
"github.com/safedep/dry/utils"
18-
"github.com/safedep/vet/gen/checks"
1918
"github.com/safedep/vet/gen/insightapi"
2019
"github.com/safedep/vet/pkg/analyzer"
2120
"github.com/safedep/vet/pkg/common"
@@ -443,40 +442,9 @@ func (r *gitLabReporter) getGitLabVulnerabilityDescription(pkg *models.Package,
443442
// getVulnerabilitySolution returns the solution for a vulnerability
444443
// Markdown formatted
445444
func (r *gitLabReporter) getGitLabVulnerabilitySolution(pkg *models.Package) string {
446-
solution := "No solution available for this vulnerability"
447-
448-
if pkg.Insights != nil && pkg.Insights.PackageCurrentVersion != nil {
449-
latestVersion := utils.SafelyGetValue(pkg.Insights.PackageCurrentVersion)
450-
solution = fmt.Sprintf("Upgrade to latest version **`%s`**", latestVersion)
451-
}
452-
453-
return solution
445+
return getVulnerabilitySolution(pkg)
454446
}
455447

456448
func (r *gitLabReporter) getPolicyViolationSolution(event *analyzer.AnalyzerEvent) string {
457-
switch event.Filter.GetCheckType() {
458-
case checks.CheckType_CheckTypeVulnerability:
459-
return r.getGitLabVulnerabilitySolution(event.Package)
460-
461-
case checks.CheckType_CheckTypePopularity:
462-
return "Consider using a more popular alternative package"
463-
464-
case checks.CheckType_CheckTypeLicense:
465-
return "Review and update package to comply with license policy"
466-
467-
case checks.CheckType_CheckTypeMaintenance:
468-
return "Update package to align with maintenance policy"
469-
470-
case checks.CheckType_CheckTypeSecurityScorecard:
471-
return "Review and improve package security posture"
472-
473-
case checks.CheckType_CheckTypeMalware:
474-
return "Remove this package and review any affected code"
475-
476-
case checks.CheckType_CheckTypeOther:
477-
return "Review and fix policy violation"
478-
479-
default:
480-
return "Review and fix policy violation"
481-
}
449+
return getPolicyViolationSolution(event)
482450
}

pkg/reporter/html.go

Lines changed: 87 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"os"
66
"path/filepath"
7+
"sort"
78
"strings"
89
"time"
910

@@ -18,11 +19,10 @@ type HtmlReportingConfig struct {
1819
Path string // Output path for HTML file
1920
}
2021

21-
type HTMLReporter struct {
22+
type htmlReporter struct {
2223
config HtmlReportingConfig
2324
manifests []*models.PackageManifest
2425
analyzerEvents []*analyzer.AnalyzerEvent
25-
policyEvents []*policy.PolicyEvent
2626
}
2727

2828
// Helper function to get NVD link from CVE ID
@@ -34,31 +34,28 @@ func getNvdLinkFromCveID(cveID string) string {
3434
}
3535

3636
func NewHtmlReporter(config HtmlReportingConfig) (Reporter, error) {
37-
return &HTMLReporter{
37+
return &htmlReporter{
3838
config: config,
3939
manifests: []*models.PackageManifest{},
4040
analyzerEvents: []*analyzer.AnalyzerEvent{},
41-
policyEvents: []*policy.PolicyEvent{},
4241
}, nil
4342
}
4443

45-
func (r *HTMLReporter) Name() string {
44+
func (r *htmlReporter) Name() string {
4645
return "html"
4746
}
4847

49-
func (r *HTMLReporter) AddManifest(manifest *models.PackageManifest) {
48+
func (r *htmlReporter) AddManifest(manifest *models.PackageManifest) {
5049
r.manifests = append(r.manifests, manifest)
5150
}
5251

53-
func (r *HTMLReporter) AddAnalyzerEvent(event *analyzer.AnalyzerEvent) {
52+
func (r *htmlReporter) AddAnalyzerEvent(event *analyzer.AnalyzerEvent) {
5453
r.analyzerEvents = append(r.analyzerEvents, event)
5554
}
5655

57-
func (r *HTMLReporter) AddPolicyEvent(event *policy.PolicyEvent) {
58-
r.policyEvents = append(r.policyEvents, event)
59-
}
56+
func (r *htmlReporter) AddPolicyEvent(event *policy.PolicyEvent) {}
6057

61-
func (r *HTMLReporter) Finish() error {
58+
func (r *htmlReporter) Finish() error {
6259
// Create the directory if it doesn't exist
6360
dir := filepath.Dir(r.config.Path)
6461
if _, err := os.Stat(dir); os.IsNotExist(err) {
@@ -107,7 +104,7 @@ func (r *HTMLReporter) Finish() error {
107104
Vulnerabilities: r.getVulnerabilities(),
108105
MalwareDetections: r.getMalwareDetections(),
109106
PackagePopularity: r.getPopularityInfo(),
110-
PolicyEvents: r.convertPolicyEvents(),
107+
PolicyViolations: r.convertPolicyViolations(),
111108
PackageCount: r.getPackageCount(),
112109
VulnCount: r.getVulnCount(),
113110
Ecosystems: r.getEcosystems(),
@@ -124,7 +121,7 @@ func (r *HTMLReporter) Finish() error {
124121

125122
// Helper methods to prepare data for the HTML template
126123

127-
func (r *HTMLReporter) getEcosystems() []string {
124+
func (r *htmlReporter) getEcosystems() []string {
128125
ecosystemsMap := make(map[string]bool)
129126

130127
for _, manifest := range r.manifests {
@@ -139,15 +136,15 @@ func (r *HTMLReporter) getEcosystems() []string {
139136
return ecosystems
140137
}
141138

142-
func (r *HTMLReporter) getPackageCount() int {
139+
func (r *htmlReporter) getPackageCount() int {
143140
count := 0
144141
for _, manifest := range r.manifests {
145142
count += len(manifest.Packages)
146143
}
147144
return count
148145
}
149146

150-
func (r *HTMLReporter) convertManifests() []templates.Manifest {
147+
func (r *htmlReporter) convertManifests() []templates.Manifest {
151148
manifests := []templates.Manifest{}
152149
for _, m := range r.manifests {
153150
packages := []templates.Package{}
@@ -167,19 +164,43 @@ func (r *HTMLReporter) convertManifests() []templates.Manifest {
167164
Packages: packages,
168165
})
169166
}
167+
168+
sort.Slice(manifests, func(i, j int) bool {
169+
return len(manifests[i].Packages) > len(manifests[j].Packages)
170+
})
170171
return manifests
171172
}
172173

173-
func (r *HTMLReporter) convertPolicyEvents() []templates.PolicyEvent {
174-
events := []templates.PolicyEvent{}
174+
func (r *htmlReporter) convertPolicyViolations() []templates.PolicyViolation {
175+
events := []templates.PolicyViolation{}
176+
177+
for _, event := range r.analyzerEvents {
178+
if !event.IsFilterMatch() {
179+
continue
180+
}
175181

176-
// Since PolicyEvent is currently empty in the policy package,
177-
// we'll have to adapt once it has actual fields
178-
// For now, return an empty slice to avoid nil issues
182+
policyViolId := fmt.Sprintf("%s-%s", gitlabCustomPolicySuffix, event.Package.Id())
183+
184+
// Create a vulnerability entry for the policy violation
185+
policyViolation := templates.PolicyViolation{
186+
ID: policyViolId,
187+
PolicyName: event.Filter.GetName(),
188+
Description: fmt.Sprintf("%s \n\n %s \n\n The CEL expression is: \n\n ```yaml\n%s\n```\n\n",
189+
event.Filter.GetSummary(),
190+
event.Filter.GetDescription(),
191+
event.Filter.GetValue(),
192+
),
193+
Solution: getPolicyViolationSolution(event),
194+
PackageName: event.Package.Name,
195+
PackageVersion: event.Package.Version,
196+
}
197+
198+
events = append(events, policyViolation)
199+
}
179200
return events
180201
}
181202

182-
func (r *HTMLReporter) getVulnerabilities() []templates.Vulnerability {
203+
func (r *htmlReporter) getVulnerabilities() []templates.Vulnerability {
183204
vulns := []templates.Vulnerability{}
184205

185206
// First try to extract vulnerabilities from package insights (preferred method)
@@ -276,14 +297,34 @@ func (r *HTMLReporter) getVulnerabilities() []templates.Vulnerability {
276297
}
277298
}
278299

300+
severityRank := map[string]int{
301+
"CRITICAL": 0,
302+
"HIGH": 1,
303+
"MEDIUM": 2,
304+
"LOW": 3,
305+
"UNKNOWN": 4,
306+
}
307+
308+
sort.Slice(vulns, func(i, j int) bool {
309+
rankI, okI := severityRank[strings.ToUpper(vulns[i].Severity)]
310+
rankJ, okJ := severityRank[strings.ToUpper(vulns[j].Severity)]
311+
if !okI {
312+
rankI = severityRank["UNKNOWN"]
313+
}
314+
if !okJ {
315+
rankJ = severityRank["UNKNOWN"]
316+
}
317+
return rankI < rankJ
318+
})
319+
279320
return vulns
280321
}
281322

282-
func (r *HTMLReporter) getVulnCount() int {
323+
func (r *htmlReporter) getVulnCount() int {
283324
return len(r.getVulnerabilities())
284325
}
285326

286-
func (r *HTMLReporter) getPackages() []templates.Package {
327+
func (r *htmlReporter) getPackages() []templates.Package {
287328
packages := []templates.Package{}
288329
vulnCountMap := make(map[string]int)
289330

@@ -320,10 +361,14 @@ func (r *HTMLReporter) getPackages() []templates.Package {
320361
}
321362
}
322363

364+
sort.Slice(packages, func(i, j int) bool {
365+
return packages[i].VulnCount > packages[j].VulnCount
366+
})
367+
323368
return packages
324369
}
325370

326-
func (r *HTMLReporter) getMalwareDetections() []templates.MalwareDetection {
371+
func (r *htmlReporter) getMalwareDetections() []templates.MalwareDetection {
327372
malware := []templates.MalwareDetection{}
328373

329374
for _, manifest := range r.manifests {
@@ -392,10 +437,27 @@ func (r *HTMLReporter) getMalwareDetections() []templates.MalwareDetection {
392437
}
393438
}
394439

440+
typeRank := map[string]int{
441+
"Malware": 0,
442+
"Suspicious": 1,
443+
"Lockfile Poisoning": 2,
444+
}
445+
446+
sort.Slice(malware, func(i, j int) bool {
447+
rankI, okI := typeRank[malware[i].Type]
448+
rankJ, okJ := typeRank[malware[j].Type]
449+
if !okI {
450+
rankI = 99 // unknown types go last
451+
}
452+
if !okJ {
453+
rankJ = 99
454+
}
455+
return rankI < rankJ
456+
})
395457
return malware
396458
}
397459

398-
func (r *HTMLReporter) getPopularityInfo() []templates.PopularityMetric {
460+
func (r *htmlReporter) getPopularityInfo() []templates.PopularityMetric {
399461
popularity := []templates.PopularityMetric{}
400462

401463
// Limit to maximum 1000 packages to avoid memory issues

0 commit comments

Comments
 (0)