Skip to content
This repository has been archived by the owner on Oct 17, 2024. It is now read-only.

Commit

Permalink
added optional flag and functionality for combined trivy output parsi…
Browse files Browse the repository at this point in the history
…ng (#115)

* added optional flag and functionality for combined trivy output parsing

* address comments
  • Loading branch information
northdpole authored Jun 17, 2021
1 parent 8236e43 commit 0ededc7
Show file tree
Hide file tree
Showing 3 changed files with 219 additions and 144 deletions.
145 changes: 87 additions & 58 deletions producers/docker_trivy/main.go
Original file line number Diff line number Diff line change
@@ -1,77 +1,106 @@
package main

import (
v1 "github.com/thought-machine/dracon/api/proto/v1"
"github.com/thought-machine/dracon/producers/docker_trivy/types"
"fmt"
"log"
"strings"
"flag"

"fmt"
"log"
"strings"

"github.com/thought-machine/dracon/producers"
v1 "github.com/thought-machine/dracon/api/proto/v1"
"github.com/thought-machine/dracon/producers/docker_trivy/types"
"github.com/thought-machine/dracon/producers"
)

var Combined bool

func main() {
if err := producers.ParseFlags(); err != nil {
log.Fatal(err)
}
flag.BoolVar(&Combined, "combinedout", false, "Output is the combined output of Trivy against multiple images, expects {<img-name>:[<regular trivy output>],<other-img>:[<trivy out for 'other-img'>]}")

if err := producers.ParseFlags(); err != nil {
log.Fatal(err)
}

inFile, err := producers.ReadInFile()
if err != nil {
log.Fatal(err)
}
inFile, err := producers.ReadInFile()
if err != nil {
log.Fatal(err)
}

var results []types.TrivyOut
if err := producers.ParseJSON(inFile, &results); err != nil {
log.Fatal(err)
}
if Combined {
var results types.CombinedOut
if err := producers.ParseJSON(inFile, &results); err != nil {
log.Fatal(err)
}
if err := producers.WriteDraconOut(
"trivy",
parseCombinedOut(results),
); err != nil {
log.Fatal(err)
}
} else {
var results []types.TrivyOut
if err := producers.ParseJSON(inFile, &results); err != nil {
log.Fatal(err)
}
if err := producers.WriteDraconOut(
"trivy",
parseSingleOut(results),
); err != nil {
log.Fatal(err)
}

if err := producers.WriteDraconOut(
"trivy",
parseOut(results),
); err != nil {
log.Fatal(err)
}
}
}
func parseCombinedOut(results types.CombinedOut) []*v1.Issue {
issues := []*v1.Issue{}
for img, output := range results {
log.Printf("Parsing Combined Output for %s\n", img)
for _, res := range output {
target := res.Target
for _, vuln := range res.Vulnerable {
issues = append(issues, parseResult(&vuln, target))
}
}
}
return issues
}

func parseOut(results []types.TrivyOut) []*v1.Issue {
issues := []*v1.Issue{}
for _, res := range results {
target := res.Target
func parseSingleOut(results []types.TrivyOut) []*v1.Issue {
issues := []*v1.Issue{}
for _, res := range results {
target := res.Target

for _, vuln := range res.Vulnerable {
issues = append(issues, parseResult(&vuln, target))
}
}
return issues
}
for _, vuln := range res.Vulnerable {
issues = append(issues, parseResult(&vuln, target))
}
}
return issues
}

// TrivySeverityToDracon maps Trivy Severity Strings to dracon struct
func TrivySeverityToDracon(severity string) v1.Severity {
switch severity {
case "LOW":
return v1.Severity_SEVERITY_LOW
case "MEDIUM":
return v1.Severity_SEVERITY_MEDIUM
case "HIGH":
return v1.Severity_SEVERITY_HIGH
case "CRITICAL":
return v1.Severity_SEVERITY_CRITICAL
default:
return v1.Severity_SEVERITY_INFO
}
}
switch severity {
case "LOW":
return v1.Severity_SEVERITY_LOW
case "MEDIUM":
return v1.Severity_SEVERITY_MEDIUM
case "HIGH":
return v1.Severity_SEVERITY_HIGH
case "CRITICAL":
return v1.Severity_SEVERITY_CRITICAL
default:
return v1.Severity_SEVERITY_INFO
}
}

func parseResult(r *types.TrivyVulnerability, target string) *v1.Issue {
cvss := r.CVSS.Nvd.V3Score
return &v1.Issue{
Target: target,
Type: "Container image vulnerability",
Title: fmt.Sprintf("[%s][%s] %s", target, r.CVE, r.Title),
Severity: TrivySeverityToDracon(r.Severity),
Confidence: v1.Confidence_CONFIDENCE_MEDIUM,
Cvss: cvss,
Description: fmt.Sprintf("CVSS Score: %v\nCvssVector: %s\nCve: %s\nCwe: %s\nReference: %s\nOriginal Description:%s\n",
r.CVSS.Nvd.V3Score, r.CVSS.Nvd.V3Vector, r.CVE, strings.Join(r.CweIDs[:], ","), r.PrimaryURL, r.Description),
}
return &v1.Issue{
Target: target,
Type: "Container image vulnerability",
Title: fmt.Sprintf("[%s][%s] %s", target, r.CVE, r.Title),
Severity: TrivySeverityToDracon(r.Severity),
Confidence: v1.Confidence_CONFIDENCE_MEDIUM,
Cvss: r.CVSS.Nvd.V3Score,
Description: fmt.Sprintf("CVSS Score: %v\nCvssVector: %s\nCve: %s\nCwe: %s\nReference: %s\nOriginal Description:%s\n",
r.CVSS.Nvd.V3Score, r.CVSS.Nvd.V3Vector, r.CVE, strings.Join(r.CweIDs[:], ","), r.PrimaryURL, r.Description),
}
}
215 changes: 129 additions & 86 deletions producers/docker_trivy/main_test.go
Original file line number Diff line number Diff line change
@@ -1,102 +1,145 @@
package main

import (
v1 "github.com/thought-machine/dracon/api/proto/v1"
"github.com/thought-machine/dracon/producers/docker_trivy/types"
"encoding/json"
"fmt"
"testing"

"encoding/json"
"fmt"
"testing"
"github.com/stretchr/testify/assert"

v1 "github.com/thought-machine/dracon/api/proto/v1"
"github.com/thought-machine/dracon/producers/docker_trivy/types"

"github.com/stretchr/testify/assert"
)

func TestParseOut(t *testing.T) {
var results []types.TrivyOut
err := json.Unmarshal([]byte(exampleOutput), &results)
if err != nil {
t.Logf(err.Error())
t.Fail()
}
issues := parseOut(results)
func TestParseCombinedOut(t *testing.T) {
var results types.CombinedOut
combinedOutput := fmt.Sprintf(`{"ubuntu:latest":%s,"alpine:latest":%s}`,exampleOutput,exampleOutput)
err := json.Unmarshal([]byte(combinedOutput), &results)
if err != nil {
t.Logf(err.Error())
t.Fail()
}
issues := parseCombinedOut(results)

expectedIssues := []*v1.Issue{&v1.Issue{
Target: "ubuntu (ubuntu 18.04)",
Type: "Container image vulnerability",
Title: "[ubuntu (ubuntu 18.04)][CVE-2020-27350] apt: integer overflows and underflows while parsing .deb packages",
Severity: v1.Severity_SEVERITY_MEDIUM,
Cvss: 5.7,
Confidence: v1.Confidence_CONFIDENCE_MEDIUM,
Description: fmt.Sprintf("CVSS Score: %v\nCvssVector: %s\nCve: %s\nCwe: %s\nReference: %s\nOriginal Description:%s\n",
"5.7", "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:L/I:L/A:L", "CVE-2020-27350",
"CWE-190", "https://avd.aquasec.com/nvd/cve-2020-27350", "APT had several integer overflows and underflows while parsing .deb packages, aka GHSL-2020-168 GHSL-2020-169, in files apt-pkg/contrib/extracttar.cc, apt-pkg/deb/debfile.cc, and apt-pkg/contrib/arfile.cc. This issue affects: apt 1.2.32ubuntu0 versions prior to 1.2.32ubuntu0.2; 1.6.12ubuntu0 versions prior to 1.6.12ubuntu0.2; 2.0.2ubuntu0 versions prior to 2.0.2ubuntu0.2; 2.1.10ubuntu0 versions prior to 2.1.10ubuntu0.1;"),
}}

found := 0
assert.Equal(t, 2, len(issues))
for _, issue := range issues {
singleMatch := 0
for _, expected := range expectedIssues {
if expected.Title == issue.Title {
singleMatch++
found++
assert.Equal(t, singleMatch, 1) //assert no duplicates
assert.EqualValues(t, expected.Type, issue.Type)
assert.EqualValues(t, expected.Title, issue.Title)
assert.EqualValues(t, expected.Severity, issue.Severity)
assert.EqualValues(t, expected.Cvss, issue.Cvss)
assert.EqualValues(t, expected.Confidence, issue.Confidence)
assert.EqualValues(t, expected.Description, issue.Description)
}
}
}
assert.Equal(t, found, len(issues)) //assert everything has been found
}

func TestParseSingleOut(t *testing.T) {
var results []types.TrivyOut
err := json.Unmarshal([]byte(exampleOutput), &results)
if err != nil {
t.Logf(err.Error())
t.Fail()
}
issues := parseSingleOut(results)

expectedIssues := make([]*v1.Issue, 1)
expectedIssues[0] = &v1.Issue{
Target: "ubuntu (ubuntu 18.04)",
Type: "Container image vulnerability",
Title: "[ubuntu (ubuntu 18.04)][CVE-2020-27350] apt: integer overflows and underflows while parsing .deb packages",
Severity: v1.Severity_SEVERITY_MEDIUM,
Cvss: 5.7,
Confidence: v1.Confidence_CONFIDENCE_MEDIUM,
Description: fmt.Sprintf("CVSS Score: %v\nCvssVector: %s\nCve: %s\nCwe: %s\nReference: %s\nOriginal Description:%s\n",
"5.7", "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:L/I:L/A:L", "CVE-2020-27350",
"CWE-190", "https://avd.aquasec.com/nvd/cve-2020-27350", "APT had several integer overflows and underflows while parsing .deb packages, aka GHSL-2020-168 GHSL-2020-169, in files apt-pkg/contrib/extracttar.cc, apt-pkg/deb/debfile.cc, and apt-pkg/contrib/arfile.cc. This issue affects: apt 1.2.32ubuntu0 versions prior to 1.2.32ubuntu0.2; 1.6.12ubuntu0 versions prior to 1.6.12ubuntu0.2; 2.0.2ubuntu0 versions prior to 2.0.2ubuntu0.2; 2.1.10ubuntu0 versions prior to 2.1.10ubuntu0.1;"),
}
expectedIssues := []*v1.Issue{&v1.Issue{
Target: "ubuntu (ubuntu 18.04)",
Type: "Container image vulnerability",
Title: "[ubuntu (ubuntu 18.04)][CVE-2020-27350] apt: integer overflows and underflows while parsing .deb packages",
Severity: v1.Severity_SEVERITY_MEDIUM,
Cvss: 5.7,
Confidence: v1.Confidence_CONFIDENCE_MEDIUM,
Description: fmt.Sprintf("CVSS Score: %v\nCvssVector: %s\nCve: %s\nCwe: %s\nReference: %s\nOriginal Description:%s\n",
"5.7", "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:L/I:L/A:L", "CVE-2020-27350",
"CWE-190", "https://avd.aquasec.com/nvd/cve-2020-27350", "APT had several integer overflows and underflows while parsing .deb packages, aka GHSL-2020-168 GHSL-2020-169, in files apt-pkg/contrib/extracttar.cc, apt-pkg/deb/debfile.cc, and apt-pkg/contrib/arfile.cc. This issue affects: apt 1.2.32ubuntu0 versions prior to 1.2.32ubuntu0.2; 1.6.12ubuntu0 versions prior to 1.6.12ubuntu0.2; 2.0.2ubuntu0 versions prior to 2.0.2ubuntu0.2; 2.1.10ubuntu0 versions prior to 2.1.10ubuntu0.1;"),
}}

found := 0
assert.Equal(t, len(expectedIssues), len(issues))
for _, issue := range issues {
singleMatch := 0
for _, expected := range expectedIssues {
if expected.Title == issue.Title {
singleMatch++
found++
assert.Equal(t, singleMatch, 1) //assert no duplicates
assert.EqualValues(t, expected.Type, issue.Type)
assert.EqualValues(t, expected.Title, issue.Title)
assert.EqualValues(t, expected.Severity, issue.Severity)
assert.EqualValues(t, expected.Cvss, issue.Cvss)
assert.EqualValues(t, expected.Confidence, issue.Confidence)
assert.EqualValues(t, expected.Description, issue.Description)
}
}
}
assert.Equal(t, found, len(issues)) //assert everything has been found
found := 0
assert.Equal(t, len(expectedIssues), len(issues))
for _, issue := range issues {
singleMatch := 0
for _, expected := range expectedIssues {
if expected.Title == issue.Title {
singleMatch++
found++
assert.Equal(t, singleMatch, 1) //assert no duplicates
assert.EqualValues(t, expected.Type, issue.Type)
assert.EqualValues(t, expected.Title, issue.Title)
assert.EqualValues(t, expected.Severity, issue.Severity)
assert.EqualValues(t, expected.Cvss, issue.Cvss)
assert.EqualValues(t, expected.Confidence, issue.Confidence)
assert.EqualValues(t, expected.Description, issue.Description)
}
}
}
assert.Equal(t, found, len(issues)) //assert everything has been found
}

var exampleOutput = `
[
{
{
"Target": "ubuntu (ubuntu 18.04)",
"Type": "ubuntu",
"Vulnerabilities": [
{
"VulnerabilityID": "CVE-2020-27350",
"PkgName": "apt",
"InstalledVersion": "1.6.12",
"FixedVersion": "1.6.12ubuntu0.2",
"Layer": {
"DiffID": "sha256:a090697502b8d19fbc83afb24d8fb59b01e48bf87763a00ca55cfff42423ad36"
},
"SeveritySource": "ubuntu",
"PrimaryURL": "https://avd.aquasec.com/nvd/cve-2020-27350",
"Title": "apt: integer overflows and underflows while parsing .deb packages",
"Description": "APT had several integer overflows and underflows while parsing .deb packages, aka GHSL-2020-168 GHSL-2020-169, in files apt-pkg/contrib/extracttar.cc, apt-pkg/deb/debfile.cc, and apt-pkg/contrib/arfile.cc. This issue affects: apt 1.2.32ubuntu0 versions prior to 1.2.32ubuntu0.2; 1.6.12ubuntu0 versions prior to 1.6.12ubuntu0.2; 2.0.2ubuntu0 versions prior to 2.0.2ubuntu0.2; 2.1.10ubuntu0 versions prior to 2.1.10ubuntu0.1;",
"Severity": "MEDIUM",
"CweIDs": [
"CWE-190"
],
"CVSS": {
"nvd": {
"V2Vector": "AV:L/AC:L/Au:N/C:P/I:P/A:P",
"V3Vector": "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:L/I:L/A:L",
"V2Score": 4.6,
"V3Score": 5.7
},
"redhat": {
"V3Vector": "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:L/I:L/A:L",
"V3Score": 5.7
}
},
"References": [
"https://bugs.launchpad.net/bugs/1899193",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-27350",
"https://security.netapp.com/advisory/ntap-20210108-0005/",
"https://usn.ubuntu.com/usn/usn-4667-1",
"https://usn.ubuntu.com/usn/usn-4667-2",
"https://www.debian.org/security/2020/dsa-4808"
],
"PublishedDate": "2020-12-10T04:15:00Z",
"LastModifiedDate": "2021-01-08T12:15:00Z"
}]}]
`
{
"VulnerabilityID": "CVE-2020-27350",
"PkgName": "apt",
"InstalledVersion": "1.6.12",
"FixedVersion": "1.6.12ubuntu0.2",
"Layer": {
"DiffID": "sha256:a090697502b8d19fbc83afb24d8fb59b01e48bf87763a00ca55cfff42423ad36"
},
"SeveritySource": "ubuntu",
"PrimaryURL": "https://avd.aquasec.com/nvd/cve-2020-27350",
"Title": "apt: integer overflows and underflows while parsing .deb packages",
"Description": "APT had several integer overflows and underflows while parsing .deb packages, aka GHSL-2020-168 GHSL-2020-169, in files apt-pkg/contrib/extracttar.cc, apt-pkg/deb/debfile.cc, and apt-pkg/contrib/arfile.cc. This issue affects: apt 1.2.32ubuntu0 versions prior to 1.2.32ubuntu0.2; 1.6.12ubuntu0 versions prior to 1.6.12ubuntu0.2; 2.0.2ubuntu0 versions prior to 2.0.2ubuntu0.2; 2.1.10ubuntu0 versions prior to 2.1.10ubuntu0.1;",
"Severity": "MEDIUM",
"CweIDs": [
"CWE-190"
],
"CVSS": {
"nvd": {
"V2Vector": "AV:L/AC:L/Au:N/C:P/I:P/A:P",
"V3Vector": "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:L/I:L/A:L",
"V2Score": 4.6,
"V3Score": 5.7
},
"redhat": {
"V3Vector": "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:L/I:L/A:L",
"V3Score": 5.7
}
},
"References": [
"https://bugs.launchpad.net/bugs/1899193",
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-27350",
"https://security.netapp.com/advisory/ntap-20210108-0005/",
"https://usn.ubuntu.com/usn/usn-4667-1",
"https://usn.ubuntu.com/usn/usn-4667-2",
"https://www.debian.org/security/2020/dsa-4808"
],
"PublishedDate": "2020-12-10T04:15:00Z",
"LastModifiedDate": "2021-01-08T12:15:00Z"
}]}]
`
Loading

0 comments on commit 0ededc7

Please sign in to comment.