Skip to content

Commit

Permalink
feat: Error handling (#158)
Browse files Browse the repository at this point in the history
* Fitering of bad purls
* Adds issue output for invalid Purls detected in SBOMS
* Simplifies json rendering
* Code tightening
* refactor loader code for individual files
* Version bump
* Clean up

---------

Co-authored-by: Mirxcle <130690850+mirxcle@users.noreply.github.com>
  • Loading branch information
djschleen and mirxcle committed Apr 14, 2023
1 parent e847795 commit ce7c346
Show file tree
Hide file tree
Showing 13 changed files with 84 additions and 58 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,6 @@
"vuln",
"vulns",
"Warningf"
]
],
"aws.codeWhisperer.shareCodeWhispererContentWithAWS": false
}
2 changes: 1 addition & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (
)

var (
version = "0.4.3"
version = "0.4.4"
output string
//Afs stores a global OS Filesystem that is used throughout bomber
Afs = &afero.Afero{Fs: afero.NewOsFs()}
Expand Down
14 changes: 11 additions & 3 deletions cmd/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,24 @@ var (
}
}
s := spinner.New(spinner.CharSets[9], 100*time.Millisecond)

purls, issues := filters.Sanitize(purls)

util.DoIf(output != "json", func() {
util.PrintInfo("Ecosystems detected:", strings.Join(ecosystems, ","))

//for each models.Issue in issues, write a message to the console
for _, issue := range issues {
util.PrintWarningf("%v (%v)\n", issue.Message, issue.Purl)
}

util.PrintInfof("Scanning %v packages for vulnerabilities...\n", len(purls))
util.PrintInfo("Vulnerability Provider:", provider.Info(), "\n")
s.Suffix = fmt.Sprintf(" Fetching vulnerability data from %s", providerName)
s.Start()
})

response, err = provider.Scan(purls, &credentials)
response, err := provider.Scan(purls, &credentials)
if err != nil {
log.Print(err)
}
Expand Down Expand Up @@ -116,8 +125,7 @@ var (
}
}
results := models.NewResults(response, severitySummary, scanned, licenses, version, providerName)
err = renderer.Render(results)
if err != nil {
if err = renderer.Render(results); err != nil {
log.Println(err)
}

Expand Down
6 changes: 0 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -288,8 +288,6 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
Expand Down Expand Up @@ -362,8 +360,6 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
Expand Down Expand Up @@ -524,8 +520,6 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
k8s.io/utils v0.0.0-20230313181309-38a27ef9d749 h1:xMMXJlJbsU8w3V5N2FLDQ8YgU8s1EoULdbQBcAeNJkY=
k8s.io/utils v0.0.0-20230313181309-38a27ef9d749/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk=
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
Expand Down
3 changes: 1 addition & 2 deletions lib/enrichment/epss.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ func Enrich(vulnerabilities []models.Vulnerability) (enriched []models.Vulnerabi
body, _ := resp.Body()
if resp.StatusCode() == 200 {
var epss models.Epss
err = json.Unmarshal(body, &epss)
if err != nil {
if err = json.Unmarshal(body, &epss); err != nil {
return
}
log.Println("EPSS response total:", epss.Total)
Expand Down
28 changes: 28 additions & 0 deletions lib/filters/purl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package filters

import (
"strings"

"github.com/package-url/packageurl-go"

"github.com/devops-kung-fu/bomber/models"
)

// Sanitize removes any invalid package URLs from the slice
func Sanitize(purls []string) (sanitized []string, issues []models.Issue) {
for _, p := range purls {
if !strings.Contains(p, "file:") {
if _, err := packageurl.FromString(p); err == nil {
sanitized = append(sanitized, p)
}
} else {
//append a new models.Issue to the issues slice
issues = append(issues, models.Issue{
IssueType: "InvalidPackageURL",
Message: "Ignoring an invalid package URL",
Purl: p,
})
}
}
return
}
28 changes: 12 additions & 16 deletions lib/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,41 +84,37 @@ func loadFilePurls(afs *afero.Afero, arg string) (scanned []models.ScannedFile,

if bytes.Contains(b, []byte("xmlns")) && bytes.Contains(b, []byte("CycloneDX")) {
log.Println("Detected CycloneDX XML")
var sbom cyclone.BOM
err = xml.Unmarshal(b, &sbom)
if err == nil {
return scanned, cyclonedx.Purls(&sbom), cyclonedx.Licenses(&sbom), err
}
return processCycloneDX(b, scanned, xml.Unmarshal)
} else if bytes.Contains(b, []byte("bomFormat")) && bytes.Contains(b, []byte("CycloneDX")) {
log.Println("Detected CycloneDX JSON")
var sbom cyclone.BOM
err = json.Unmarshal(b, &sbom)
if err == nil {
l := cyclonedx.Licenses(&sbom)
return scanned, cyclonedx.Purls(&sbom), l, err
}
return processCycloneDX(b, scanned, json.Unmarshal)
} else if bytes.Contains(b, []byte("SPDXRef-DOCUMENT")) {
log.Println("Detected SPDX")
var sbom spdx.BOM
err = json.Unmarshal(b, &sbom)
if err == nil {
if err = json.Unmarshal(b, &sbom); err == nil {
return scanned, sbom.Purls(), sbom.Licenses(), err
}
} else if bytes.Contains(b, []byte("https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-")) {
log.Println("Detected Syft")
var sbom syft.BOM
err = json.Unmarshal(b, &sbom)
if err == nil {
if err = json.Unmarshal(b, &sbom); err == nil {
return scanned, sbom.Purls(), sbom.Licenses(), err
}
}
log.Printf("WARNING: %v isn't a valid SBOM", arg)
return scanned, nil, nil, fmt.Errorf("%v is not a SBOM recognized by bomber", arg)
}

func processCycloneDX(b []byte, s []models.ScannedFile, unmarshal func([]byte, interface{}) error) (scanned []models.ScannedFile, purls []string, licenses []string, err error) {
var sbom cyclone.BOM
if err = unmarshal(b, &sbom); err == nil {
return s, cyclonedx.Purls(&sbom), cyclonedx.Licenses(&sbom), err
}
return
}

// LoadIgnore loads a list of CVEs entered one on each line from the filename provided
func LoadIgnore(afs *afero.Afero, ignoreFile string) (cves []string, err error) {

f, err := afs.Open(ignoreFile)
if err != nil {
log.Printf("error opening ignore: %v\n", err)
Expand Down
19 changes: 10 additions & 9 deletions lib/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,25 @@ import (
"github.com/devops-kung-fu/bomber/models"
)

// Rating converts a CVSS score to a string
// Rating takes a CVSS score as input and returns a rating string based on the score
func Rating(score float64) string {
if score > 0 && score <= 3.9 {
switch {
case score > 0 && score <= 3.9:
return "LOW"
} else if score >= 4.0 && score <= 6.9 {
case score >= 4.0 && score <= 6.9:
return "MODERATE"
} else if score >= 7.0 && score <= 8.9 {
case score >= 7.0 && score <= 8.9:
return "HIGH"
} else if score >= 9.0 && score <= 10.0 {
case score >= 9.0 && score <= 10.0:
return "CRITICAL"
default:
return "UNSPECIFIED"
}
return "UNSPECIFIED"
}

// AdjustSummary increments the counts of severities in a Summary struct based on the severity string passed in
// AdjustSummary takes a severity string and a pointer to a Summary struct as input, and increments the corresponding severity count in the struct.
func AdjustSummary(severity string, summary *models.Summary) {
severity = strings.ToUpper(severity)
switch severity {
switch strings.ToUpper(severity) {
case "LOW":
summary.Low++
case "MODERATE":
Expand Down
8 changes: 8 additions & 0 deletions models/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,11 @@ type EpssScore struct {
Percentile string `json:"percentile,omitempty"`
Date string `json:"date,omitempty"`
}

// Issue encapsulates an issue with the processing of an SBOM
type Issue struct {
Err error `json:"error,omitempty"`
IssueType string `json:"issueType,omitempty"`
Purl string `json:"purl,omitempty"`
Message string `json:"message,omitempty"`
}
10 changes: 5 additions & 5 deletions providers/ossindex/OSSIndex.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,8 @@ func (Provider) Info() string {

// Scan scans a slice of Purls for vulnerabilities against the OSS Index
func (Provider) Scan(purls []string, credentials *models.Credentials) (packages []models.Package, err error) {
err = validateCredentials(credentials)
if err != nil {
return
if err = validateCredentials(credentials); err != nil {
return nil, fmt.Errorf("could not validate credentials: %w", err)
}
totalPurls := len(purls)
for startIndex := 0; startIndex < totalPurls; startIndex += 128 {
Expand All @@ -56,8 +55,7 @@ func (Provider) Scan(purls []string, credentials *models.Credentials) (packages
body, _ := resp.Body()
if resp.StatusCode() == 200 {
var response []models.Package
err = json.Unmarshal(body, &response)
if err != nil {
if err = json.Unmarshal(body, &response); err != nil {
return
}
for i, pkg := range response {
Expand All @@ -82,9 +80,11 @@ func validateCredentials(credentials *models.Credentials) (err error) {
if credentials == nil {
return errors.New("credentials cannot be nil")
}

if credentials.Username == "" {
credentials.Username = os.Getenv("BOMBER_PROVIDER_USERNAME")
}

if credentials.Token == "" {
credentials.Token = os.Getenv("BOMBER_PROVIDER_TOKEN")
}
Expand Down
1 change: 0 additions & 1 deletion providers/snyk/snyk.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ func (Provider) Scan(purls []string, credentials *models.Credentials) (packages
if err = validateCredentials(credentials); err != nil {
return packages, fmt.Errorf("could not validate credentials: %w", err)
}

wg := sizedwaitgroup.New(Concurrency)
client := newClient(credentials)
orgID, err := getOrgID(client)
Expand Down
18 changes: 5 additions & 13 deletions renderers/json/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
package json

import (
"bytes"
"encoding/json"
"fmt"
"log"
Expand All @@ -13,21 +12,14 @@ import (
// Renderer contains methods to render to JSON format
type Renderer struct{}

// Render renders pretty printed JSON to the STDOUT
func (Renderer) Render(results models.Results) (err error) {
b, err := json.Marshal(results)
// Render outputs json to STDOUT
func (Renderer) Render(results models.Results) error {
b, err := json.MarshalIndent(results, "", "\t")
if err != nil {
log.Println(err)
return err
}

var prettyJSON bytes.Buffer
error := json.Indent(&prettyJSON, b, "", "\t")
if error != nil {
log.Println("JSON parse error: ", error)
return err
}

fmt.Println(prettyJSON.String())
return
fmt.Println(string(b))
return nil
}
2 changes: 1 addition & 1 deletion renderers/stdout/stdout.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func (Renderer) Render(results models.Results) (err error) {
if len(results.Files) > 0 {
util.PrintInfo("Files Scanned")
for _, scanned := range results.Files {
util.PrintTabbedf("%s (sha256:%s)", scanned.Name, scanned.SHA256)
util.PrintTabbedf("%s (sha256:%s)\n", scanned.Name, scanned.SHA256)
}
fmt.Println()

Expand Down

0 comments on commit ce7c346

Please sign in to comment.