Skip to content

Commit

Permalink
fix: Fixes issue where a license expression wasn't being utilized (#108)
Browse files Browse the repository at this point in the history
* Fixes issue where a license expression wasn't being utilized

* Improves test coverage
  • Loading branch information
djschleen committed Jan 6, 2023
1 parent 9ee85bb commit 1a421ad
Show file tree
Hide file tree
Showing 14 changed files with 1,128 additions and 702 deletions.
9 changes: 9 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"configurations": [



{
"name": "Debug Folder (OSV)",
"type": "go",
Expand All @@ -14,6 +15,14 @@
"program": "${workspaceFolder}/main.go",
"args": ["--debug=true", "scan", "./sbom/test"]
},
{
"name": "Debug Expression License (OSV)",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}/main.go",
"args": ["--debug=true", "scan", "./sbom/test/expression-license.json"]
},
{
"name": "Debug Folder (ossindex)",
"type": "go",
Expand Down
2 changes: 2 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"ignoretests",
"incredibles",
"Infof",
"jarcoal",
"jedib",
"JSONAPI",
"kirinlabs",
Expand All @@ -38,6 +39,7 @@
"snyk",
"Sonatype",
"SPDXID",
"stretchr",
"structs",
"Syft",
"synk",
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ So you've asked a vendor for an Software Bill of Materials (SBOM) for one of the

The first thing you're going to want to do is see if any of the components listed inside the SBOM have security vulnerabilities, and what kind of licenses these components have. This will help you identify what kind of risk you will be taking on by using the product.

Finding security vulnerabilities and license information for components identified in an SBOM is exactly what ```bomber``` is meant to do. ```bomber``` can read any JSON or XML based [CycloneDX](https://cyclonedx.org) format, or a JSON [SPDX](https://spdx.dev) or [Syft](https://github.com/anchore/syft) formatted SBOM, and tell you pretty quickly if there are any vulnerabilities.
Finding security vulnerabilities and license information for components identified in a SBOM is exactly what ```bomber``` is meant to do. ```bomber``` can read any JSON or XML based [CycloneDX](https://cyclonedx.org) format, or a JSON [SPDX](https://spdx.dev) or [Syft](https://github.com/anchore/syft) formatted SBOM, and tell you pretty quickly if there are any vulnerabilities.

### Open vs. Closed Source

Expand Down Expand Up @@ -165,13 +165,13 @@ If you wish, you can set two environment variables to store your credentials, an

### Scanning SBOMs from STDIN

If you're using ```bomber``` in your CI/CD pipelines, you can do an all in one command with Syft to generate and scan an SBOM for vulnerabilities. To do this, you can do something like the following command:
If you're using ```bomber``` in your CI/CD pipelines, you can do an all in one command with Syft to generate and scan a SBOM for vulnerabilities. To do this, you can do something like the following command:

``` bash
# Make sure you include the - character at the end of the command. This triggers bomber to read from STDIN
syft packages . -o cyclonedx-json | bomber scan --provider ossindex --output json -
```
This command creates an SBOM, pipes it into bomber, and generates results in JSON format.
This command creates a SBOM, pipes it into bomber, and generates results in JSON format.

### Environment Variables

Expand Down Expand Up @@ -208,6 +208,6 @@ A big thank-you to our friends at [Smashicons](https://www.flaticon.com/authors/

Thank you to [Sonatype](https://sonatype.com) for providing a wicked tool like the [Sonatype OSS Index](https://ossindex.sonatype.org).

Many thanks to our friends and fellow ```bomber``` contributors at [Snyk](https://snyk.io) for creating a provider, and coding up processing an SBOM from STDIN. You guys rock.
Many thanks to our friends and fellow ```bomber``` contributors at [Snyk](https://snyk.io) for creating a provider, and coding up processing a SBOM from STDIN. You guys rock.

EPSS description comes from the team at [Nucleus](https://nucleussec.com/blog/what-is-epss/). Thank you!
1 change: 1 addition & 0 deletions cmd/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ var (
for _, p := range purls {
purl, err := packageurl.FromString(p)
if err != nil {
util.PrintErr(err)
log.Println(err)
}
if !slices.Contains(ecosystems, purl.Type) {
Expand Down
13 changes: 9 additions & 4 deletions formats/cyclonedx/cyclonedx.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@ func Licenses(bom *cyclone.BOM) (licenses []string) {
for _, component := range *bom.Components {
if component.Licenses != nil {
for _, licenseChoice := range *component.Licenses {
if licenseChoice.License.ID != "" {
if licenseChoice.Expression != "" {
licenses = append(licenses, licenseChoice.Expression)
}
if licenseChoice.License != nil && licenseChoice.License.ID != "" {
licenses = append(licenses, licenseChoice.License.ID)
}
}
}

}
return
}
Expand Down Expand Up @@ -58,9 +60,12 @@ func TestBytes() []byte {
"licenses": [
{
"license": {
"id": "MIT"
"id": "MIT"
}
}
},
{
"expression": "(AFL-2.1 OR BSD-3-Clause)"
}
],
"properties": [{
"name": "syft:package:metadataType",
Expand Down
2 changes: 1 addition & 1 deletion formats/cyclonedx/cyclonedx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ func TestLicenses(t *testing.T) {

licenses := Licenses(&sbom)

assert.Len(t, licenses, 1)
assert.Len(t, licenses, 2)
}
12 changes: 7 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.19

require (
github.com/CycloneDX/cyclonedx-go v0.7.0
github.com/briandowns/spinner v1.19.0
github.com/briandowns/spinner v1.20.0
github.com/devops-kung-fu/common v0.2.5
github.com/gookit/color v1.5.2
github.com/jarcoal/httpmock v1.2.0
Expand All @@ -19,6 +19,8 @@ require (
k8s.io/utils v0.0.0-20221128185143-99ec85e7a448
)

require golang.org/x/term v0.4.0 // indirect

require (
github.com/aymerick/douceur v0.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
Expand All @@ -28,16 +30,16 @@ require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.4.3 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
golang.org/x/net v0.4.0 // indirect
golang.org/x/sys v0.3.0 // indirect
golang.org/x/text v0.5.0 // indirect
golang.org/x/net v0.5.0 // indirect
golang.org/x/sys v0.4.0 // indirect
golang.org/x/text v0.6.0 // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
22 changes: 13 additions & 9 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ github.com/CycloneDX/cyclonedx-go v0.7.0/go.mod h1:W5Z9w8pTTL+t+yG3PCiFRGlr8PUlE
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M=
github.com/briandowns/spinner v1.19.0 h1:s8aq38H+Qju89yhp89b4iIiMzMm8YN3p6vGpwyh/a8E=
github.com/briandowns/spinner v1.19.0/go.mod h1:mQak9GHqbspjC/5iUx3qMlIho8xBS/ppAL/hX5SmPJU=
github.com/briandowns/spinner v1.20.0 h1:GQq1Yf1KyzYT8CY19GzWrDKP6hYOFB6J72Ks7d8aO1U=
github.com/briandowns/spinner v1.20.0/go.mod h1:TcwZHb7Wb6vn/+bcVv1UXEzaA4pLS7yznHlkY/HzH44=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
Expand Down Expand Up @@ -161,8 +161,9 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
Expand Down Expand Up @@ -288,8 +289,8 @@ golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
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.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU=
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
Expand Down Expand Up @@ -348,17 +349,20 @@ golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg=
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
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.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
Expand Down
5 changes: 3 additions & 2 deletions lib/bomloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ func loadFilePurls(afs *afero.Afero, arg string) (scanned []models.ScannedFile,
var sbom cyclone.BOM
err = json.Unmarshal(b, &sbom)
if err == nil {
return scanned, cyclonedx.Purls(&sbom), cyclonedx.Licenses(&sbom), err
l := cyclonedx.Licenses(&sbom)
return scanned, cyclonedx.Purls(&sbom), l, err
}
} else if bytes.Contains(b, []byte("SPDXRef-DOCUMENT")) {
log.Println("Detected SPDX")
Expand All @@ -111,5 +112,5 @@ func loadFilePurls(afs *afero.Afero, arg string) (scanned []models.ScannedFile,
}
}
log.Printf("WARNING: %v isn't a valid SBOM", arg)
return scanned, nil, nil, fmt.Errorf("%v is not an SBOM recognized by bomber", arg)
return scanned, nil, nil, fmt.Errorf("%v is not a SBOM recognized by bomber", arg)
}
28 changes: 26 additions & 2 deletions lib/bomloader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ func TestLoad_cyclonedx(t *testing.T) {

files, _ := afs.ReadDir("/")
assert.Len(t, files, 1)

scanned, purls, _, err := Load(afs, []string{"/"})

assert.NotNil(t, scanned)
Expand Down Expand Up @@ -112,7 +113,7 @@ func TestLoad_BadJSON_SPDX(t *testing.T) {

_, _, _, err = loadFilePurls(afs, "/test-spdx.json")
assert.Error(t, err)
assert.Equal(t, "/test-spdx.json is not an SBOM recognized by bomber", err.Error())
assert.Equal(t, "/test-spdx.json is not a SBOM recognized by bomber", err.Error())
}

func TestLoad_garbage(t *testing.T) {
Expand All @@ -123,7 +124,7 @@ func TestLoad_garbage(t *testing.T) {

_, _, _, err = loadFilePurls(afs, "/not-a-sbom.json")
assert.Error(t, err)
assert.Equal(t, "/not-a-sbom.json is not an SBOM recognized by bomber", err.Error())
assert.Equal(t, "/not-a-sbom.json is not a SBOM recognized by bomber", err.Error())
}

func Test_loadFilePurls(t *testing.T) {
Expand All @@ -132,3 +133,26 @@ func Test_loadFilePurls(t *testing.T) {
_, _, _, err := loadFilePurls(afs, "no-file.json")
assert.Error(t, err)
}

func TestLoad_multiple_cyclonedx(t *testing.T) {
afs := &afero.Afero{Fs: afero.NewMemMapFs()}

err := afs.WriteFile("/test-cyclonedx.json", cyclonedx.TestBytes(), 0644)
assert.NoError(t, err)

err = afs.WriteFile("/test1/test1-cyclonedx.json", cyclonedx.TestBytes(), 0644)
assert.NoError(t, err)

err = afs.WriteFile("/test2/test2-cyclonedx.json", cyclonedx.TestBytes(), 0644)
assert.NoError(t, err)

scanned, purls, _, err := Load(afs, []string{"/"})

assert.NotNil(t, scanned)
assert.NoError(t, err)
assert.Len(t, purls, 1)
assert.Equal(t, "pkg:golang/github.com/CycloneDX/cyclonedx-go@v0.6.0", purls[0])

_, err = afs.ReadDir("/bad-dir")
assert.Error(t, err)
}
75 changes: 37 additions & 38 deletions renderers/stdout/stdout.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@ type Renderer struct{}

// Render renders a pretty tabular summary to STDOUT
func (Renderer) Render(results models.Results) (err error) {
if len(results.Packages) == 0 {
return
}
if len(results.Files) > 0 {
util.PrintInfo("Files Scanned")
for _, scanned := range results.Files {
Expand All @@ -37,44 +34,46 @@ func (Renderer) Render(results models.Results) (err error) {
fmt.Println()
}
vulnCount := vulnerabilityCount(results.Packages)
log.Println("Rendering Packages:", len(results.Packages))
t := table.NewWriter()
t.SetOutputMirror(os.Stdout)
rowConfigAutoMerge := table.RowConfig{AutoMerge: true}
t.AppendHeader(table.Row{"Type", "Name", "Version", "Severity", "Vulnerability", "EPSS %"}, rowConfigAutoMerge)
for _, r := range results.Packages {
purl, err := packageurl.FromString(r.Purl)
if err != nil {
log.Println(err)
}
for _, v := range r.Vulnerabilities {
p, _ := strconv.ParseFloat(v.Epss.Percentile, 64)
percentage := math.Round(p * 100)
percentageString := "N/A"
if percentage > 0 {
percentageString = fmt.Sprintf("%d%%", uint64(percentage))
if len(results.Packages) != 0 {
log.Println("Rendering Packages:", len(results.Packages))
t := table.NewWriter()
t.SetOutputMirror(os.Stdout)
rowConfigAutoMerge := table.RowConfig{AutoMerge: true}
t.AppendHeader(table.Row{"Type", "Name", "Version", "Severity", "Vulnerability", "EPSS %"}, rowConfigAutoMerge)
for _, r := range results.Packages {
purl, err := packageurl.FromString(r.Purl)
if err != nil {
log.Println(err)
}
for _, v := range r.Vulnerabilities {
p, _ := strconv.ParseFloat(v.Epss.Percentile, 64)
percentage := math.Round(p * 100)
percentageString := "N/A"
if percentage > 0 {
percentageString = fmt.Sprintf("%d%%", uint64(percentage))
}
t.AppendRow([]interface{}{purl.Type, purl.Name, purl.Version, v.Severity, v.ID, percentageString}, rowConfigAutoMerge)
}
t.AppendRow([]interface{}{purl.Type, purl.Name, purl.Version, v.Severity, v.ID, percentageString}, rowConfigAutoMerge)
}
t.SetStyle(table.StyleRounded)
t.SetColumnConfigs([]table.ColumnConfig{
{
Name: "Description",
WidthMin: 6,
WidthMax: 64,
},
})
t.SortBy([]table.SortBy{
{Name: "Name", Mode: table.Dsc},
{Name: "Severity", Mode: table.Dsc},
})
t.SetColumnConfigs([]table.ColumnConfig{
{Number: 1, AutoMerge: true},
{Number: 2, AutoMerge: true},
})
t.Style().Options.SeparateRows = true
t.Render()
}
t.SetStyle(table.StyleRounded)
t.SetColumnConfigs([]table.ColumnConfig{
{
Name: "Description",
WidthMin: 6,
WidthMax: 64,
},
})
t.SortBy([]table.SortBy{
{Name: "Name", Mode: table.Dsc},
{Name: "Severity", Mode: table.Dsc},
})
t.SetColumnConfigs([]table.ColumnConfig{
{Number: 1, AutoMerge: true},
{Number: 2, AutoMerge: true},
})
t.Style().Options.SeparateRows = true
t.Render()
if vulnCount > 0 {
fmt.Println()
color.Red.Printf("Total vulnerabilities found: %v\n", vulnCount)
Expand Down

0 comments on commit 1a421ad

Please sign in to comment.