4
4
"fmt"
5
5
"os"
6
6
"path/filepath"
7
+ "sort"
7
8
"strings"
8
9
"time"
9
10
@@ -18,11 +19,10 @@ type HtmlReportingConfig struct {
18
19
Path string // Output path for HTML file
19
20
}
20
21
21
- type HTMLReporter struct {
22
+ type htmlReporter struct {
22
23
config HtmlReportingConfig
23
24
manifests []* models.PackageManifest
24
25
analyzerEvents []* analyzer.AnalyzerEvent
25
- policyEvents []* policy.PolicyEvent
26
26
}
27
27
28
28
// Helper function to get NVD link from CVE ID
@@ -34,31 +34,28 @@ func getNvdLinkFromCveID(cveID string) string {
34
34
}
35
35
36
36
func NewHtmlReporter (config HtmlReportingConfig ) (Reporter , error ) {
37
- return & HTMLReporter {
37
+ return & htmlReporter {
38
38
config : config ,
39
39
manifests : []* models.PackageManifest {},
40
40
analyzerEvents : []* analyzer.AnalyzerEvent {},
41
- policyEvents : []* policy.PolicyEvent {},
42
41
}, nil
43
42
}
44
43
45
- func (r * HTMLReporter ) Name () string {
44
+ func (r * htmlReporter ) Name () string {
46
45
return "html"
47
46
}
48
47
49
- func (r * HTMLReporter ) AddManifest (manifest * models.PackageManifest ) {
48
+ func (r * htmlReporter ) AddManifest (manifest * models.PackageManifest ) {
50
49
r .manifests = append (r .manifests , manifest )
51
50
}
52
51
53
- func (r * HTMLReporter ) AddAnalyzerEvent (event * analyzer.AnalyzerEvent ) {
52
+ func (r * htmlReporter ) AddAnalyzerEvent (event * analyzer.AnalyzerEvent ) {
54
53
r .analyzerEvents = append (r .analyzerEvents , event )
55
54
}
56
55
57
- func (r * HTMLReporter ) AddPolicyEvent (event * policy.PolicyEvent ) {
58
- r .policyEvents = append (r .policyEvents , event )
59
- }
56
+ func (r * htmlReporter ) AddPolicyEvent (event * policy.PolicyEvent ) {}
60
57
61
- func (r * HTMLReporter ) Finish () error {
58
+ func (r * htmlReporter ) Finish () error {
62
59
// Create the directory if it doesn't exist
63
60
dir := filepath .Dir (r .config .Path )
64
61
if _ , err := os .Stat (dir ); os .IsNotExist (err ) {
@@ -107,7 +104,7 @@ func (r *HTMLReporter) Finish() error {
107
104
Vulnerabilities : r .getVulnerabilities (),
108
105
MalwareDetections : r .getMalwareDetections (),
109
106
PackagePopularity : r .getPopularityInfo (),
110
- PolicyEvents : r . convertPolicyEvents (),
107
+ PolicyViolations : r . convertPolicyViolations (),
111
108
PackageCount : r .getPackageCount (),
112
109
VulnCount : r .getVulnCount (),
113
110
Ecosystems : r .getEcosystems (),
@@ -124,7 +121,7 @@ func (r *HTMLReporter) Finish() error {
124
121
125
122
// Helper methods to prepare data for the HTML template
126
123
127
- func (r * HTMLReporter ) getEcosystems () []string {
124
+ func (r * htmlReporter ) getEcosystems () []string {
128
125
ecosystemsMap := make (map [string ]bool )
129
126
130
127
for _ , manifest := range r .manifests {
@@ -139,15 +136,15 @@ func (r *HTMLReporter) getEcosystems() []string {
139
136
return ecosystems
140
137
}
141
138
142
- func (r * HTMLReporter ) getPackageCount () int {
139
+ func (r * htmlReporter ) getPackageCount () int {
143
140
count := 0
144
141
for _ , manifest := range r .manifests {
145
142
count += len (manifest .Packages )
146
143
}
147
144
return count
148
145
}
149
146
150
- func (r * HTMLReporter ) convertManifests () []templates.Manifest {
147
+ func (r * htmlReporter ) convertManifests () []templates.Manifest {
151
148
manifests := []templates.Manifest {}
152
149
for _ , m := range r .manifests {
153
150
packages := []templates.Package {}
@@ -167,19 +164,43 @@ func (r *HTMLReporter) convertManifests() []templates.Manifest {
167
164
Packages : packages ,
168
165
})
169
166
}
167
+
168
+ sort .Slice (manifests , func (i , j int ) bool {
169
+ return len (manifests [i ].Packages ) > len (manifests [j ].Packages )
170
+ })
170
171
return manifests
171
172
}
172
173
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
+ }
175
181
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
+ }
179
200
return events
180
201
}
181
202
182
- func (r * HTMLReporter ) getVulnerabilities () []templates.Vulnerability {
203
+ func (r * htmlReporter ) getVulnerabilities () []templates.Vulnerability {
183
204
vulns := []templates.Vulnerability {}
184
205
185
206
// First try to extract vulnerabilities from package insights (preferred method)
@@ -276,14 +297,34 @@ func (r *HTMLReporter) getVulnerabilities() []templates.Vulnerability {
276
297
}
277
298
}
278
299
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
+
279
320
return vulns
280
321
}
281
322
282
- func (r * HTMLReporter ) getVulnCount () int {
323
+ func (r * htmlReporter ) getVulnCount () int {
283
324
return len (r .getVulnerabilities ())
284
325
}
285
326
286
- func (r * HTMLReporter ) getPackages () []templates.Package {
327
+ func (r * htmlReporter ) getPackages () []templates.Package {
287
328
packages := []templates.Package {}
288
329
vulnCountMap := make (map [string ]int )
289
330
@@ -320,10 +361,14 @@ func (r *HTMLReporter) getPackages() []templates.Package {
320
361
}
321
362
}
322
363
364
+ sort .Slice (packages , func (i , j int ) bool {
365
+ return packages [i ].VulnCount > packages [j ].VulnCount
366
+ })
367
+
323
368
return packages
324
369
}
325
370
326
- func (r * HTMLReporter ) getMalwareDetections () []templates.MalwareDetection {
371
+ func (r * htmlReporter ) getMalwareDetections () []templates.MalwareDetection {
327
372
malware := []templates.MalwareDetection {}
328
373
329
374
for _ , manifest := range r .manifests {
@@ -392,10 +437,27 @@ func (r *HTMLReporter) getMalwareDetections() []templates.MalwareDetection {
392
437
}
393
438
}
394
439
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
+ })
395
457
return malware
396
458
}
397
459
398
- func (r * HTMLReporter ) getPopularityInfo () []templates.PopularityMetric {
460
+ func (r * htmlReporter ) getPopularityInfo () []templates.PopularityMetric {
399
461
popularity := []templates.PopularityMetric {}
400
462
401
463
// Limit to maximum 1000 packages to avoid memory issues
0 commit comments