Skip to content

Commit 6d42b81

Browse files
feat: group vulnerabilities based on os/base image (#50)
* feat: using standard severity instead of severity * fix: remove dml on cve_store and handle it in code handling this versioning * fix: minor fix * feat: group vulnerabilities based on os/base image * fix: minor fix * chore: move test file to tests folder
1 parent 3dc4167 commit 6d42b81

File tree

7 files changed

+127
-26
lines changed

7 files changed

+127
-26
lines changed

internal/step-lib/util/common-util/util.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ func ReadFile(fileName string) ([]byte, error) {
6969
func ParseJsonTemplate(inputTemplate string, data []byte) (string, error) {
7070
tmpl := template.Must(template.New("").Funcs(template.FuncMap{
7171
"add": func(a, b int) int { return a + b },
72+
"len": func(sliceIf interface{}) int {
73+
if slice, ok := sliceIf.([]any); ok {
74+
return len(slice)
75+
}
76+
return 0
77+
},
7278
}).Parse(inputTemplate))
7379
jsonMap := map[string]interface{}{}
7480
err := json.Unmarshal(data, &jsonMap)

pkg/security/ImageScanService.go

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ func (impl *ImageScanServiceImpl) RegisterScanExecutionHistoryAndState(scanEvent
274274
impl.Logger.Errorw("Failed to save executionHistory", "err", err, "model", executionHistoryModel)
275275
return nil, executionHistoryDirPath, err
276276
}
277+
277278
// creating folder for storing all details if not exist
278279
isExist, err := helper.DoesFileExist(bean.ScanOutputDirectory)
279280
if err != nil {
@@ -522,7 +523,7 @@ func (impl *ImageScanServiceImpl) ConvertEndStepOutputAndSaveVulnerabilities(ste
522523

523524
imageScanExecutionResults := make([]*repository.ImageScanExecutionResult, 0, len(vulnerabilities))
524525
for _, vul := range vulnerabilities {
525-
imageScanExecutionResult := createImageScanExecutionResultObject(executionHistoryId, vul.Name, vul.Package, vul.PackageVersion, vul.FixedInVersion, tool.Id)
526+
imageScanExecutionResult := createImageScanExecutionResultObject(executionHistoryId, vul.Name, vul.Package, vul.PackageVersion, vul.FixedInVersion, vul.Class, vul.Type, vul.TargetName, tool.Id)
526527
imageScanExecutionResults = append(imageScanExecutionResults, imageScanExecutionResult)
527528
}
528529
tx, err := impl.CveStoreRepository.GetConnection().Begin()
@@ -562,7 +563,7 @@ func (impl *ImageScanServiceImpl) ConvertEndStepOutputAndSaveVulnerabilities(ste
562563
}
563564

564565
func isV1Template(resultDescriptorTemplate string) bool {
565-
var mappings []bean.Mapping
566+
var mappings []map[string]interface{}
566567
err := json.Unmarshal([]byte(resultDescriptorTemplate), &mappings)
567568
return err != nil && isValidGoTemplate(resultDescriptorTemplate) //checking error too because our new result descriptor template can pass go templating too as it contains a simple json
568569
}
@@ -593,33 +594,46 @@ func (impl *ImageScanServiceImpl) getImageScanOutputObjectsV1(stepOutput []byte,
593594

594595
func (impl *ImageScanServiceImpl) getImageScanOutputObjectsV2(stepOutput []byte, resultDescriptorTemplate string) ([]*bean.ImageScanOutputObject, error) {
595596
var vulnerabilities []*bean.ImageScanOutputObject
596-
var mappings []bean.Mapping
597+
var mappings []map[string]interface{}
597598
err := json.Unmarshal([]byte(resultDescriptorTemplate), &mappings)
598599
if err != nil {
599600
impl.Logger.Errorw("error in un-marshaling result descriptor template", "err", err, "resultDescriptorTemplate", resultDescriptorTemplate)
600601
return nil, err
601602
}
602-
var processArray func(mapping bean.Mapping, value gjson.Result)
603-
processArray = func(mapping bean.Mapping, value gjson.Result) {
603+
var processArray func(mapping map[string]interface{}, value gjson.Result)
604+
processArray = func(mapping map[string]interface{}, value gjson.Result) {
605+
vulnerabilitiesPath := mapping[bean.MappingKeyPathToVulnerabilitiesArray].(string)
606+
vulnerabilityDataKeyPathsMap := mapping[bean.MappingKeyPathToVulnerabilityDataKeys].(map[string]interface{})
607+
resultDataKeyPathsMap := mapping[bean.MappingKeyPathToResultDataKeys].(map[string]interface{})
608+
604609
value.ForEach(func(_, nestedValue gjson.Result) bool {
605-
if nestedValue.IsArray() {
606-
// if the nested value is an array, recursively process it
607-
processArray(mapping, nestedValue)
608-
} else {
609-
vulnerability := &bean.ImageScanOutputObject{
610-
Name: nestedValue.Get(mapping[bean.MappingKeyName]).String(),
611-
Package: nestedValue.Get(mapping[bean.MappingKeyPackage]).String(),
612-
PackageVersion: nestedValue.Get(mapping[bean.MappingKeyPackageVersion]).String(),
613-
FixedInVersion: nestedValue.Get(mapping[bean.MappingKeyFixedInVersion]).String(),
614-
Severity: nestedValue.Get(mapping[bean.MappingKeySeverity]).String(),
610+
targetName, class, resType := "", "", ""
611+
if nestedValue.IsObject() {
612+
targetName, class, resType = nestedValue.Get(resultDataKeyPathsMap[bean.MappingTarget].(string)).String(), nestedValue.Get(resultDataKeyPathsMap[bean.MappingClass].(string)).String(), nestedValue.Get(resultDataKeyPathsMap[bean.MappingType].(string)).String()
613+
614+
if nestedValue.Get(vulnerabilitiesPath).IsArray() {
615+
nestedValue.Get(vulnerabilitiesPath).ForEach(func(_, vul gjson.Result) bool {
616+
vulnerability := &bean.ImageScanOutputObject{
617+
Name: vul.Get(vulnerabilityDataKeyPathsMap[bean.MappingKeyName].(string)).String(),
618+
Package: vul.Get(vulnerabilityDataKeyPathsMap[bean.MappingKeyPackage].(string)).String(),
619+
PackageVersion: vul.Get(vulnerabilityDataKeyPathsMap[bean.MappingKeyPackageVersion].(string)).String(),
620+
FixedInVersion: vul.Get(vulnerabilityDataKeyPathsMap[bean.MappingKeyFixedInVersion].(string)).String(),
621+
Severity: vul.Get(vulnerabilityDataKeyPathsMap[bean.MappingKeySeverity].(string)).String(),
622+
TargetName: targetName,
623+
Class: class,
624+
Type: resType,
625+
}
626+
vulnerabilities = append(vulnerabilities, vulnerability)
627+
return true
628+
})
615629
}
616-
vulnerabilities = append(vulnerabilities, vulnerability)
617630
}
618631
return true
619632
})
620633
}
634+
621635
for _, mapping := range mappings {
622-
result := gjson.Get(string(stepOutput), mapping[bean.MappingKeyPathToVulnerabilitiesArray])
636+
result := gjson.Get(string(stepOutput), mapping[bean.MappingKeyPathToResultsArray].(string))
623637
if !result.Exists() {
624638
continue
625639
}
@@ -740,7 +754,7 @@ func (impl *ImageScanServiceImpl) CreateScanExecutionRegistryForClairV4(vs []*cl
740754
cvesToUpdate = append(cvesToUpdate, cveStore)
741755
}
742756
}
743-
imageScanExecutionResult := createImageScanExecutionResultObject(executionHistory.Id, item.Name, item.Package.Name, item.Package.Version, item.FixedInVersion, toolId)
757+
imageScanExecutionResult := createImageScanExecutionResultObject(executionHistory.Id, item.Name, item.Package.Name, item.Package.Version, item.FixedInVersion, "", "", "", toolId)
744758
imageScanExecutionResultsToBeSaved = append(imageScanExecutionResultsToBeSaved, imageScanExecutionResult)
745759
}
746760
tx, err := impl.CveStoreRepository.GetConnection().Begin()
@@ -803,7 +817,7 @@ func (impl *ImageScanServiceImpl) CreateScanExecutionRegistryForClairV2(vs []*cl
803817
cvesToUpdate = append(cvesToUpdate, cveStore)
804818
}
805819
}
806-
imageScanExecutionResult := createImageScanExecutionResultObject(executionHistory.Id, item.Name, item.FeatureName, item.FeatureVersion, item.FixedBy, toolId)
820+
imageScanExecutionResult := createImageScanExecutionResultObject(executionHistory.Id, item.Name, item.FeatureName, item.FeatureVersion, item.FixedBy, "", "", "", toolId)
807821
imageScanExecutionResultsToBeSaved = append(imageScanExecutionResultsToBeSaved, imageScanExecutionResult)
808822
}
809823
tx, err := impl.CveStoreRepository.GetConnection().Begin()

pkg/security/helper.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,16 @@ func createCveStoreObject(name, version, fixedInVersion, severity string, userId
1818
return cveStore
1919
}
2020

21-
func createImageScanExecutionResultObject(executionHistoryId int, vulName, packageName, version, fixedInVersion string, toolId int) *repository.ImageScanExecutionResult {
21+
func createImageScanExecutionResultObject(executionHistoryId int, vulName, packageName, version, fixedInVersion, className, typeName, targetName string, toolId int) *repository.ImageScanExecutionResult {
2222
return &repository.ImageScanExecutionResult{
2323
ImageScanExecutionHistoryId: executionHistoryId,
2424
CveStoreName: vulName,
2525
Package: packageName,
2626
ScanToolId: toolId,
2727
Version: version,
2828
FixedVersion: fixedInVersion,
29+
Target: targetName,
30+
Type: typeName,
31+
Class: className,
2932
}
3033
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package security
2+
3+
import (
4+
"fmt"
5+
"github.com/devtron-labs/image-scanner/pkg/logger"
6+
"github.com/tidwall/gjson"
7+
"os"
8+
"testing"
9+
)
10+
11+
const jsonKeys = "[ \n { \n \"pathToResultArray\": \"Results\",\n \"pathToVulnerabilitiesArray\": \"Vulnerabilities\",\n \"vulnerabilityData\":{\n \t\t\"name\": \"VulnerabilityID\",\n \t\"package\": \"PkgName\", \n \t\"packageVersion\": \"InstalledVersion\",\n \t\"fixedInVersion\": \"FixedVersion\",\n \t\"severity\": \"Severity\"\n },\n \"resultData\":{\n \t\t\t\"target\":\"Target\",\n \t\"class\":\"Class\",\n \t\"type\":\"Type\" \n }\n }\n]"
12+
13+
func TestScan(t *testing.T) {
14+
bytes, err := os.ReadFile("testTrivyScanResultV2.json")
15+
if err != nil {
16+
t.Fail()
17+
}
18+
19+
logger, _ := logger.InitLogger()
20+
impl := ImageScanServiceImpl{Logger: logger}
21+
out, err := impl.getImageScanOutputObjectsV2(bytes, jsonKeys)
22+
fmt.Println(out, err)
23+
if err != nil || len(out) == 0 {
24+
t.Fail()
25+
}
26+
27+
cnt, err := countVuln(bytes)
28+
if err != nil {
29+
t.Fail()
30+
}
31+
32+
if cnt != len(out) {
33+
t.Fail()
34+
}
35+
}
36+
37+
func countVuln(jsonStr []byte) (int, error) {
38+
result := gjson.Get(string(jsonStr), "Results")
39+
if !result.Exists() {
40+
return 0, nil
41+
}
42+
43+
targetCountMap := make(map[string]int)
44+
45+
result.ForEach(func(_, nestedValue gjson.Result) bool {
46+
if nestedValue.IsObject() {
47+
count := 0
48+
targetName := nestedValue.Get("Target").String()
49+
if nestedValue.Get("Vulnerabilities").IsArray() {
50+
nestedValue.Get("Vulnerabilities").ForEach(func(_, vul gjson.Result) bool {
51+
count++
52+
return true
53+
})
54+
}
55+
targetCountMap[targetName] += count
56+
}
57+
return true
58+
})
59+
60+
count := 0
61+
for _, cnt := range targetCountMap {
62+
count += cnt
63+
}
64+
return count, nil
65+
}

pkg/sql/bean/bean.go

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ const (
5050
)
5151

5252
type ImageScanOutputObject struct {
53+
TargetName string `json:"targetName"`
54+
Class string `json:"class"`
55+
Type string `json:"type"`
5356
Name string `json:"name"`
5457
Package string `json:"package"`
5558
PackageVersion string `json:"packageVersion"`
@@ -61,12 +64,18 @@ type ImageScanOutputObject struct {
6164
type Mapping map[string]string
6265

6366
const (
64-
MappingKeyPathToVulnerabilitiesArray = "pathToVulnerabilitiesArray"
65-
MappingKeyName = "name"
66-
MappingKeyPackage = "package"
67-
MappingKeyPackageVersion = "packageVersion"
68-
MappingKeyFixedInVersion = "fixedInVersion"
69-
MappingKeySeverity = "severity"
67+
MappingKeyPathToResultDataKeys = "resultData"
68+
MappingKeyPathToVulnerabilityDataKeys = "vulnerabilityData"
69+
MappingKeyPathToResultsArray = "pathToResultArray"
70+
MappingKeyPathToVulnerabilitiesArray = "pathToVulnerabilitiesArray"
71+
MappingKeyName = "name"
72+
MappingKeyPackage = "package"
73+
MappingKeyPackageVersion = "packageVersion"
74+
MappingKeyFixedInVersion = "fixedInVersion"
75+
MappingKeySeverity = "severity"
76+
MappingTarget = "target"
77+
MappingType = "type"
78+
MappingClass = "class"
7079
)
7180

7281
type Severity int

pkg/sql/repository/ImageScanResultRepository.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ type ImageScanExecutionResult struct {
3030
Package string `sql:"package"`
3131
Version string `sql:"version"`
3232
FixedVersion string `sql:"fixed_version"`
33+
Target string `sql:"target"`
34+
Type string `sql:"type"`
35+
Class string `sql:"class"`
3336
CveStore CveStore
3437
ImageScanExecutionHistory ImageScanExecutionHistory
3538
}

tests/testTrivyScanResultV2.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}

0 commit comments

Comments
 (0)