-
Notifications
You must be signed in to change notification settings - Fork 152
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Grype vulnerability scanner parsing tool (#2114)
* Added parsing tool * Added associated UTs * Added comments * Addressed Pavan's review comments 1. Moved the parsing tool and associated UTs to a new folder pkg/tools 2. Fixed the func docs 3. unexported all types from the parsing tool 4. Fixed variable names 5. Printed the entire error stack 6. Fixed spelling issues * Addressed Pavan's review comments 1. changed the input flag variable name 2. changed test input name 3. changed Id to ID in vulnerabilityReport struct 4. fixed error strings to match the rest of the codebase * Final suggestions from Pavan --------- Co-authored-by: Pavan Navarathna <6504783+pavannd1@users.noreply.github.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
- Loading branch information
1 parent
fdb232c
commit 30e033e
Showing
5 changed files
with
272 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/json" | ||
"flag" | ||
"fmt" | ||
"os" | ||
"strings" | ||
) | ||
|
||
type vulnerabilityScannerResponse struct { | ||
Matches json.RawMessage `json:"matches"` | ||
RelatedVulnerabilities json.RawMessage `json:"relatedVulnerabilities"` | ||
MatchDetails json.RawMessage `json:"matchDetails"` | ||
Artifact json.RawMessage `json:"artifact"` | ||
} | ||
|
||
type matchResponse struct { | ||
Vulnerabilities vulnerabilityReport `json:"vulnerability"` | ||
} | ||
|
||
type fixVersionsResponse struct { | ||
Versions []string `json:"versions"` | ||
State string `json:"state"` | ||
} | ||
|
||
type vulnerabilityReport struct { | ||
ID string `json:"id"` | ||
Severity string `json:"severity"` | ||
Namespace string `json:"namespace"` | ||
Description string `json:"description"` | ||
FixVersions fixVersionsResponse `json:"fix"` | ||
} | ||
|
||
// filterVulnerabilityReportMatches filters vulnerabilities based on the severity levels set in severityTypeSet | ||
func filterVulnerabilityReportMatches(matches []matchResponse, severityTypeSet map[string]bool) ([]vulnerabilityReport, error) { | ||
mv := make([]vulnerabilityReport, 0) | ||
for _, m := range matches { | ||
if severityTypeSet[m.Vulnerabilities.Severity] { | ||
mv = append(mv, m.Vulnerabilities) | ||
} | ||
} | ||
return mv, nil | ||
} | ||
|
||
// decodeVulnerabilityReports unmarshals the specific matches from the vulnerability report | ||
// and returns a list of vulnerabilities based on the severity levels set in severityTypeSet | ||
func decodeVulnerabilityReports(v vulnerabilityScannerResponse, severityTypeSet map[string]bool) ([]vulnerabilityReport, error) { | ||
var mr []matchResponse | ||
mv := make([]vulnerabilityReport, 0) | ||
if err := json.Unmarshal(v.Matches, &mr); err != nil { | ||
return mv, fmt.Errorf("failed to unmarshal matches: %v", err) | ||
} | ||
return filterVulnerabilityReportMatches(mr, severityTypeSet) | ||
} | ||
|
||
// parseVulerabilitiesReport unmarshals the vulnerability report and returns a list of vulnerabilities | ||
// based on the severity levels set in severityTypeSet | ||
func parseVulerabilitiesReport(filePath string, severityLevels []string) ([]vulnerabilityReport, error) { | ||
mv := make([]vulnerabilityReport, 0) | ||
data, err := os.ReadFile(filePath) | ||
if err != nil { | ||
return mv, fmt.Errorf("failed to read file at path %s: %v", filePath, err) | ||
} | ||
var response vulnerabilityScannerResponse | ||
|
||
if err = json.Unmarshal(data, &response); err != nil { | ||
return mv, fmt.Errorf("failed to unmarshal response: %v", err) | ||
} | ||
severityTypeSet := make(map[string]bool) | ||
for _, severityLevel := range severityLevels { | ||
severityTypeSet[severityLevel] = true | ||
} | ||
return decodeVulnerabilityReports(response, severityTypeSet) | ||
} | ||
|
||
// printResult Displays the filtered list of vulnerability reports to stdout | ||
func printResult(mv []vulnerabilityReport) { | ||
for _, vulnerability := range mv { | ||
fmt.Printf("ID: %s\n", vulnerability.ID) | ||
fmt.Printf("Severity: %s\n", vulnerability.Severity) | ||
fmt.Printf("Namespace: %s\n", vulnerability.Namespace) | ||
fmt.Printf("Description: %s\n", vulnerability.Description) | ||
fmt.Printf("Fix Versions: %v\n", vulnerability.FixVersions) | ||
fmt.Printf("\n") | ||
} | ||
} | ||
|
||
func main() { | ||
validSeverityLevels := []string{"Negliable", "Low", "Medium", "High", "Critical"} | ||
severityInputList := flag.String("s", "High,Critical", "Comma separated list of severity levels to scan. Valid severity levels are: "+strings.Join(validSeverityLevels, ",")) | ||
reportJsonFilePath := flag.String("p", "", "Path to the JSON file containing the vulnerabilities report") | ||
flag.Parse() | ||
|
||
// passing file path is compulsory | ||
if *reportJsonFilePath == "" { | ||
flag.PrintDefaults() | ||
os.Exit(1) | ||
} | ||
severityLevels := strings.Split(*severityInputList, ",") | ||
mv, err := parseVulerabilitiesReport(*reportJsonFilePath, severityLevels) | ||
if err != nil { | ||
fmt.Printf("Failed to parse vulnerabilities report: %v\n", err) | ||
os.Exit(1) | ||
} | ||
fmt.Printf("Found %d vulnerabilities\n", len(mv)) | ||
if len(mv) == 0 { | ||
os.Exit(0) | ||
} | ||
printResult(mv) | ||
os.Exit(1) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package main | ||
|
||
import ( | ||
"testing" | ||
|
||
. "gopkg.in/check.v1" | ||
) | ||
|
||
type VulnerabilityParserSuite struct { | ||
} | ||
|
||
// Hook up gocheck into the "go test" runner. | ||
func Test(t *testing.T) { TestingT(t) } | ||
|
||
var _ = Suite(&VulnerabilityParserSuite{}) | ||
|
||
func (v *VulnerabilityParserSuite) TestNonExistentResult(c *C) { | ||
severityLevels := []string{"High", "Critical"} | ||
matchingVulnerabilities, err := parseVulerabilitiesReport("mock_data/result_non_existent.json", severityLevels) | ||
c.Assert(len(matchingVulnerabilities), Equals, 0) | ||
c.Assert(err, NotNil) | ||
} | ||
|
||
func (v *VulnerabilityParserSuite) TestInvalidJson(c *C) { | ||
severityLevels := []string{"High", "Critical"} | ||
matchingVulnerabilities, err := parseVulerabilitiesReport("mock_data/results_invalid.json", severityLevels) | ||
c.Assert(len(matchingVulnerabilities), Equals, 0) | ||
c.Assert(err, NotNil) | ||
} | ||
|
||
func (v *VulnerabilityParserSuite) TestValidJsonWithZeroVulnerabilities(c *C) { | ||
severityLevels := []string{"High", "Critical"} | ||
matchingVulnerabilities, err := parseVulerabilitiesReport("mock_data/results_valid_no_matches.json", severityLevels) | ||
c.Assert(len(matchingVulnerabilities), Equals, 0) | ||
c.Assert(err, IsNil) | ||
} | ||
|
||
func (v *VulnerabilityParserSuite) TestValidJsonForLowVulerabilities(c *C) { | ||
severityLevels := []string{"Low", "Medium"} | ||
matchingVulnerabilities, err := parseVulerabilitiesReport("mock_data/results_valid.json", severityLevels) | ||
c.Assert(len(matchingVulnerabilities), Equals, 0) | ||
c.Assert(err, IsNil) | ||
} | ||
func (v *VulnerabilityParserSuite) TestValidJsonForMatchingVulerabilities(c *C) { | ||
severityLevels := []string{"High", "Critical"} | ||
expectedIds := []string{"CVE-2016-10228", "CVE-2016-10229"} | ||
matchingVulnerabilities, err := parseVulerabilitiesReport("mock_data/results_valid.json", severityLevels) | ||
c.Assert(len(matchingVulnerabilities), Equals, 2) | ||
c.Assert(err, IsNil) | ||
for index, vulnerability := range matchingVulnerabilities { | ||
c.Assert(vulnerability.ID, Equals, expectedIds[index]) | ||
c.Assert(vulnerability.Severity, Equals, severityLevels[index]) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
{ | ||
"matches": [ | ||
{ | ||
"vulnerability": { | ||
"id": "CVE-2016-10228", | ||
"dataSource": "https://security-tracker.debian.org/tracker/CVE-2016-10228", | ||
"namespace": "debian:distro:debian:10", | ||
"severity": "Medium", | ||
"urls": [ | ||
"https://security-tracker.debian.org/tracker/CVE-2016-10228" | ||
], | ||
"description": "The iconv program in the GNU C Library (aka glibc or libc6) 2.31 and earlier, when invoked with multiple suffixes in the destination encoding (TRANSLATE or IGNORE) along with the -c option, enters an infinite loop when processing invalid multi-byte input sequences, leading to a denial of service.", | ||
"cvss": [], | ||
"fix": { | ||
"versions": [ | ||
"2.28-10+deb10u2" | ||
], | ||
"state": "fixed" | ||
}, | ||
"advisories": [] | ||
}, | ||
"relatedVulnerabilities": [], | ||
"matchDetails":[], | ||
"artifact": {} | ||
}, | ||
{ | ||
"vulnerability": { | ||
"id": "CVE-2016-10229", | ||
"dataSource": "https://security-tracker.debian.org/tracker/CVE-2016-10228", | ||
"namespace": "debian:distro:debian:10", | ||
"severity": "High", | ||
"urls": { | ||
"https://security-tracker.debian.org/tracker/CVE-2016-10228" | ||
], | ||
"description": "The iconv program in the GNU C Library (aka glibc or libc6) 2.31 and earlier, when invoked with multiple suffixes in the destination encoding (TRANSLATE or IGNORE) along with the -c option, enters an infinite loop when processing invalid multi-byte input sequences, leading to a denial of service.", | ||
"cvss": [], | ||
"fix": { | ||
"versions": [ | ||
"2.28-10+deb10u2" | ||
], | ||
"state": "fixed" | ||
}, | ||
"advisories": [] | ||
}, | ||
"relatedVulnerabilities": [], | ||
"matchDetails":[], | ||
"artifact": {} | ||
} | ||
] | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
{ | ||
"matches": [ | ||
{ | ||
"vulnerability": { | ||
"id": "CVE-2016-10228", | ||
"dataSource": "https://security-tracker.debian.org/tracker/CVE-2016-10228", | ||
"namespace": "debian:distro:debian:10", | ||
"severity": "High", | ||
"urls": [ | ||
"https://security-tracker.debian.org/tracker/CVE-2016-10228" | ||
], | ||
"description": "The iconv program in the GNU C Library (aka glibc or libc6) 2.31 and earlier, when invoked with multiple suffixes in the destination encoding (TRANSLATE or IGNORE) along with the -c option, enters an infinite loop when processing invalid multi-byte input sequences, leading to a denial of service.", | ||
"cvss": [], | ||
"fix": { | ||
"versions": [ | ||
"2.28-10+deb10u2" | ||
], | ||
"state": "fixed" | ||
}, | ||
"advisories": [] | ||
}, | ||
"relatedVulnerabilities": [], | ||
"matchDetails":[], | ||
"artifact": {} | ||
}, | ||
{ | ||
"vulnerability": { | ||
"id": "CVE-2016-10229", | ||
"dataSource": "https://security-tracker.debian.org/tracker/CVE-2016-10228", | ||
"namespace": "debian:distro:debian:10", | ||
"severity": "Critical", | ||
"urls": [ | ||
"https://security-tracker.debian.org/tracker/CVE-2016-10228" | ||
], | ||
"description": "The iconv program in the GNU C Library (aka glibc or libc6) 2.31 and earlier, when invoked with multiple suffixes in the destination encoding (TRANSLATE or IGNORE) along with the -c option, enters an infinite loop when processing invalid multi-byte input sequences, leading to a denial of service.", | ||
"cvss": [], | ||
"fix": { | ||
"versions": [ | ||
"2.28-10+deb10u2" | ||
], | ||
"state": "fixed" | ||
}, | ||
"advisories": [] | ||
}, | ||
"relatedVulnerabilities": [], | ||
"matchDetails":[], | ||
"artifact": {} | ||
} | ||
] | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"matches": [] | ||
} | ||
|