mirror of
https://github.com/anchore/grype
synced 2024-11-10 06:34:13 +00:00
feat: add db search subcommand (#2031)
Signed-off-by: Tomer Seinfeld <tomersein@gmail.com>
This commit is contained in:
parent
89c4190914
commit
e7ceffadc8
6 changed files with 174 additions and 30 deletions
|
@ -31,6 +31,7 @@ func DB(app clio.Application) *cobra.Command {
|
|||
DBList(app),
|
||||
DBStatus(app),
|
||||
DBUpdate(app),
|
||||
DBSearch(app),
|
||||
)
|
||||
|
||||
return db
|
||||
|
|
116
cmd/grype/cli/commands/db_search.go
Normal file
116
cmd/grype/cli/commands/db_search.go
Normal file
|
@ -0,0 +1,116 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/olekukonko/tablewriter"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/anchore/clio"
|
||||
"github.com/anchore/grype/grype"
|
||||
"github.com/anchore/grype/grype/vulnerability"
|
||||
"github.com/anchore/grype/internal/bus"
|
||||
"github.com/anchore/grype/internal/log"
|
||||
)
|
||||
|
||||
type dbQueryOptions struct {
|
||||
Output string `yaml:"output" json:"output" mapstructure:"output"`
|
||||
DBOptions `yaml:",inline" mapstructure:",squash"`
|
||||
}
|
||||
|
||||
var _ clio.FlagAdder = (*dbQueryOptions)(nil)
|
||||
|
||||
func (c *dbQueryOptions) AddFlags(flags clio.FlagSet) {
|
||||
flags.StringVarP(&c.Output, "output", "o", "format to display results (available=[table, json])")
|
||||
}
|
||||
|
||||
func DBSearch(app clio.Application) *cobra.Command {
|
||||
opts := &dbQueryOptions{
|
||||
Output: "table",
|
||||
DBOptions: *dbOptionsDefault(app.ID()),
|
||||
}
|
||||
|
||||
return app.SetupCommand(&cobra.Command{
|
||||
Use: "search [vulnerability_id]",
|
||||
Short: "get information on a vulnerability from the db",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(_ *cobra.Command, args []string) (err error) {
|
||||
id := args[0]
|
||||
return runDBSearch(opts, id)
|
||||
},
|
||||
}, opts)
|
||||
}
|
||||
|
||||
func runDBSearch(opts *dbQueryOptions, vulnerabilityID string) error {
|
||||
log.Debug("loading DB")
|
||||
str, status, dbCloser, err := grype.LoadVulnerabilityDB(opts.DB.ToCuratorConfig(), opts.DB.AutoUpdate)
|
||||
err = validateDBLoad(err, status)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dbCloser != nil {
|
||||
defer dbCloser.Close()
|
||||
}
|
||||
|
||||
vulnerabilities, err := str.Get(vulnerabilityID, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sb := &strings.Builder{}
|
||||
if len(vulnerabilities) == 0 {
|
||||
return fmt.Errorf("vulnerability doesn't exist in the DB: %s", vulnerabilityID)
|
||||
}
|
||||
|
||||
err = present(opts.Output, vulnerabilities, sb)
|
||||
bus.Report(sb.String())
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func present(outputFormat string, vulnerabilities []vulnerability.Vulnerability, output io.Writer) error {
|
||||
if vulnerabilities == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch outputFormat {
|
||||
case "table":
|
||||
rows := [][]string{}
|
||||
for _, v := range vulnerabilities {
|
||||
rows = append(rows, []string{v.ID, v.PackageName, v.Namespace, v.Constraint.String()})
|
||||
}
|
||||
|
||||
table := tablewriter.NewWriter(output)
|
||||
columns := []string{"ID", "Package Name", "Namespace", "Version Constraint"}
|
||||
|
||||
table.SetHeader(columns)
|
||||
table.SetAutoWrapText(false)
|
||||
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
|
||||
table.SetAlignment(tablewriter.ALIGN_LEFT)
|
||||
|
||||
table.SetHeaderLine(false)
|
||||
table.SetBorder(false)
|
||||
table.SetAutoFormatHeaders(true)
|
||||
table.SetCenterSeparator("")
|
||||
table.SetColumnSeparator("")
|
||||
table.SetRowSeparator("")
|
||||
table.SetTablePadding(" ")
|
||||
table.SetNoWhiteSpace(true)
|
||||
|
||||
table.AppendBulk(rows)
|
||||
table.Render()
|
||||
case "json":
|
||||
enc := json.NewEncoder(output)
|
||||
enc.SetEscapeHTML(false)
|
||||
enc.SetIndent("", " ")
|
||||
if err := enc.Encode(vulnerabilities); err != nil {
|
||||
return fmt.Errorf("failed to encode diff information: %+v", err)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unsupported output format: %s", outputFormat)
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -99,7 +99,13 @@ func (s *store) GetVulnerabilityNamespaces() ([]string, error) {
|
|||
func (s *store) GetVulnerability(namespace, id string) ([]v5.Vulnerability, error) {
|
||||
var models []model.VulnerabilityModel
|
||||
|
||||
result := s.db.Where("namespace = ? AND id = ?", namespace, id).Find(&models)
|
||||
query := s.db.Where("id = ?", id)
|
||||
|
||||
if namespace != "" {
|
||||
query = query.Where("namespace = ?", namespace)
|
||||
}
|
||||
|
||||
result := query.Find(&models)
|
||||
|
||||
var vulnerabilities = make([]v5.Vulnerability, len(models))
|
||||
for idx, m := range models {
|
||||
|
|
|
@ -33,6 +33,7 @@ func Test_GetByDistro(t *testing.T) {
|
|||
|
||||
expected := []vulnerability.Vulnerability{
|
||||
{
|
||||
PackageName: "neutron",
|
||||
Constraint: version.MustGetConstraint("< 2014.1.3-6", version.DebFormat),
|
||||
ID: "CVE-2014-fake-1",
|
||||
Namespace: "debian:distro:debian:8",
|
||||
|
@ -41,6 +42,7 @@ func Test_GetByDistro(t *testing.T) {
|
|||
Advisories: []vulnerability.Advisory{},
|
||||
},
|
||||
{
|
||||
PackageName: "neutron",
|
||||
Constraint: version.MustGetConstraint("< 2013.0.2-1", version.DebFormat),
|
||||
ID: "CVE-2013-fake-2",
|
||||
Namespace: "debian:distro:debian:8",
|
||||
|
@ -87,8 +89,9 @@ func Test_GetByCPE(t *testing.T) {
|
|||
cpe: cpe.Must("cpe:2.3:*:activerecord:activerecord:*:*:*:*:*:ruby:*:*", ""),
|
||||
expected: []vulnerability.Vulnerability{
|
||||
{
|
||||
Constraint: version.MustGetConstraint("< 3.7.4", version.UnknownFormat),
|
||||
ID: "CVE-2014-fake-4",
|
||||
PackageName: "activerecord",
|
||||
Constraint: version.MustGetConstraint("< 3.7.4", version.UnknownFormat),
|
||||
ID: "CVE-2014-fake-4",
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must("cpe:2.3:*:activerecord:activerecord:*:*:something:*:*:ruby:*:*", ""),
|
||||
},
|
||||
|
@ -103,8 +106,9 @@ func Test_GetByCPE(t *testing.T) {
|
|||
cpe: cpe.Must("cpe:2.3:*:ActiVERecord:ACTiveRecord:*:*:*:*:*:ruby:*:*", ""),
|
||||
expected: []vulnerability.Vulnerability{
|
||||
{
|
||||
Constraint: version.MustGetConstraint("< 3.7.4", version.UnknownFormat),
|
||||
ID: "CVE-2014-fake-4",
|
||||
PackageName: "activerecord",
|
||||
Constraint: version.MustGetConstraint("< 3.7.4", version.UnknownFormat),
|
||||
ID: "CVE-2014-fake-4",
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must("cpe:2.3:*:activerecord:activerecord:*:*:something:*:*:ruby:*:*", ""),
|
||||
},
|
||||
|
@ -119,8 +123,9 @@ func Test_GetByCPE(t *testing.T) {
|
|||
cpe: cpe.Must("cpe:2.3:*:activerecord:activerecord:*:*:*:*:*:*:*:*", ""),
|
||||
expected: []vulnerability.Vulnerability{
|
||||
{
|
||||
Constraint: version.MustGetConstraint("< 3.7.6", version.UnknownFormat),
|
||||
ID: "CVE-2014-fake-3",
|
||||
PackageName: "activerecord",
|
||||
Constraint: version.MustGetConstraint("< 3.7.6", version.UnknownFormat),
|
||||
ID: "CVE-2014-fake-3",
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must("cpe:2.3:*:activerecord:activerecord:*:*:*:*:*:rails:*:*", ""),
|
||||
},
|
||||
|
@ -129,8 +134,9 @@ func Test_GetByCPE(t *testing.T) {
|
|||
Advisories: []vulnerability.Advisory{},
|
||||
},
|
||||
{
|
||||
Constraint: version.MustGetConstraint("< 3.7.4", version.UnknownFormat),
|
||||
ID: "CVE-2014-fake-4",
|
||||
PackageName: "activerecord",
|
||||
Constraint: version.MustGetConstraint("< 3.7.4", version.UnknownFormat),
|
||||
ID: "CVE-2014-fake-4",
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must("cpe:2.3:*:activerecord:activerecord:*:*:something:*:*:ruby:*:*", ""),
|
||||
},
|
||||
|
@ -183,6 +189,7 @@ func Test_Get(t *testing.T) {
|
|||
expected := []vulnerability.Vulnerability{
|
||||
{
|
||||
Constraint: version.MustGetConstraint("< 2014.1.3-6", version.DebFormat),
|
||||
PackageName: "neutron",
|
||||
ID: "CVE-2014-fake-1",
|
||||
Namespace: "debian:distro:debian:8",
|
||||
PackageQualifiers: []qualifier.Qualifier{},
|
||||
|
|
|
@ -15,6 +15,7 @@ type Reference struct {
|
|||
}
|
||||
|
||||
type Vulnerability struct {
|
||||
PackageName string
|
||||
Constraint version.Constraint
|
||||
PackageQualifiers []qualifier.Qualifier
|
||||
CPEs []cpe.CPE
|
||||
|
@ -55,6 +56,7 @@ func NewVulnerability(vuln grypeDB.Vulnerability) (*Vulnerability, error) {
|
|||
}
|
||||
|
||||
return &Vulnerability{
|
||||
PackageName: vuln.PackageName,
|
||||
Constraint: constraint,
|
||||
ID: vuln.ID,
|
||||
CPEs: make([]cpe.CPE, 0),
|
||||
|
|
|
@ -391,6 +391,7 @@ func TestVulnerabilityMatcher_FindMatches(t *testing.T) {
|
|||
wantMatches: match.NewMatches(
|
||||
match.Match{
|
||||
Vulnerability: vulnerability.Vulnerability{
|
||||
PackageName: "neutron",
|
||||
Constraint: version.MustGetConstraint("< 2014.1.3-6", version.DebFormat),
|
||||
ID: "CVE-2014-fake-1",
|
||||
Namespace: "debian:distro:debian:8",
|
||||
|
@ -444,6 +445,7 @@ func TestVulnerabilityMatcher_FindMatches(t *testing.T) {
|
|||
wantMatches: match.NewMatches(
|
||||
match.Match{
|
||||
Vulnerability: vulnerability.Vulnerability{
|
||||
PackageName: "neutron",
|
||||
Constraint: version.MustGetConstraint("< 2014.1.3-6", version.DebFormat),
|
||||
ID: "CVE-2014-fake-1",
|
||||
Namespace: "debian:distro:debian:8",
|
||||
|
@ -524,6 +526,7 @@ func TestVulnerabilityMatcher_FindMatches(t *testing.T) {
|
|||
},
|
||||
Match: match.Match{
|
||||
Vulnerability: vulnerability.Vulnerability{
|
||||
PackageName: "neutron",
|
||||
Constraint: version.MustGetConstraint("< 2014.1.3-6", version.DebFormat),
|
||||
ID: "CVE-2014-fake-1",
|
||||
Namespace: "debian:distro:debian:8",
|
||||
|
@ -572,9 +575,10 @@ func TestVulnerabilityMatcher_FindMatches(t *testing.T) {
|
|||
wantMatches: match.NewMatches(
|
||||
match.Match{
|
||||
Vulnerability: vulnerability.Vulnerability{
|
||||
Constraint: version.MustGetConstraint("< 3.7.6", version.UnknownFormat),
|
||||
ID: "CVE-2014-fake-3",
|
||||
Namespace: "nvd:cpe",
|
||||
PackageName: "activerecord",
|
||||
Constraint: version.MustGetConstraint("< 3.7.6", version.UnknownFormat),
|
||||
ID: "CVE-2014-fake-3",
|
||||
Namespace: "nvd:cpe",
|
||||
CPEs: []cpe.CPE{
|
||||
mustCPE("cpe:2.3:*:activerecord:activerecord:*:*:*:*:*:rails:*:*"),
|
||||
},
|
||||
|
@ -609,9 +613,10 @@ func TestVulnerabilityMatcher_FindMatches(t *testing.T) {
|
|||
},
|
||||
match.Match{
|
||||
Vulnerability: vulnerability.Vulnerability{
|
||||
Constraint: version.MustGetConstraint("< 3.7.6", version.UnknownFormat),
|
||||
ID: "GHSA-2014-fake-3",
|
||||
Namespace: "github:language:ruby",
|
||||
PackageName: "activerecord",
|
||||
Constraint: version.MustGetConstraint("< 3.7.6", version.UnknownFormat),
|
||||
ID: "GHSA-2014-fake-3",
|
||||
Namespace: "github:language:ruby",
|
||||
RelatedVulnerabilities: []vulnerability.Reference{
|
||||
{
|
||||
ID: "CVE-2014-fake-3",
|
||||
|
@ -666,9 +671,10 @@ func TestVulnerabilityMatcher_FindMatches(t *testing.T) {
|
|||
wantMatches: match.NewMatches(
|
||||
match.Match{
|
||||
Vulnerability: vulnerability.Vulnerability{
|
||||
Constraint: version.MustGetConstraint("< 3.7.6", version.UnknownFormat),
|
||||
ID: "CVE-2014-fake-3",
|
||||
Namespace: "nvd:cpe",
|
||||
PackageName: "activerecord",
|
||||
Constraint: version.MustGetConstraint("< 3.7.6", version.UnknownFormat),
|
||||
ID: "CVE-2014-fake-3",
|
||||
Namespace: "nvd:cpe",
|
||||
CPEs: []cpe.CPE{
|
||||
mustCPE("cpe:2.3:*:activerecord:activerecord:*:*:*:*:*:rails:*:*"),
|
||||
},
|
||||
|
@ -752,9 +758,10 @@ func TestVulnerabilityMatcher_FindMatches(t *testing.T) {
|
|||
wantMatches: match.NewMatches(
|
||||
match.Match{
|
||||
Vulnerability: vulnerability.Vulnerability{
|
||||
Constraint: version.MustGetConstraint("< 3.7.6", version.UnknownFormat),
|
||||
ID: "CVE-2014-fake-3",
|
||||
Namespace: "nvd:cpe",
|
||||
PackageName: "activerecord",
|
||||
Constraint: version.MustGetConstraint("< 3.7.6", version.UnknownFormat),
|
||||
ID: "CVE-2014-fake-3",
|
||||
Namespace: "nvd:cpe",
|
||||
CPEs: []cpe.CPE{
|
||||
mustCPE("cpe:2.3:*:activerecord:activerecord:*:*:*:*:*:rails:*:*"),
|
||||
},
|
||||
|
@ -793,6 +800,7 @@ func TestVulnerabilityMatcher_FindMatches(t *testing.T) {
|
|||
{
|
||||
Match: match.Match{
|
||||
Vulnerability: vulnerability.Vulnerability{
|
||||
PackageName: "activerecord",
|
||||
Constraint: version.MustGetConstraint("< 3.7.6", version.UnknownFormat),
|
||||
ID: "CVE-2014-fake-3",
|
||||
Namespace: "nvd:cpe",
|
||||
|
@ -861,9 +869,10 @@ func TestVulnerabilityMatcher_FindMatches(t *testing.T) {
|
|||
{
|
||||
Match: match.Match{
|
||||
Vulnerability: vulnerability.Vulnerability{
|
||||
Constraint: version.MustGetConstraint("< 3.7.6", version.UnknownFormat),
|
||||
ID: "CVE-2014-fake-3",
|
||||
Namespace: "nvd:cpe",
|
||||
PackageName: "activerecord",
|
||||
Constraint: version.MustGetConstraint("< 3.7.6", version.UnknownFormat),
|
||||
ID: "CVE-2014-fake-3",
|
||||
Namespace: "nvd:cpe",
|
||||
CPEs: []cpe.CPE{
|
||||
mustCPE("cpe:2.3:*:activerecord:activerecord:*:*:*:*:*:rails:*:*"),
|
||||
},
|
||||
|
@ -911,6 +920,7 @@ func TestVulnerabilityMatcher_FindMatches(t *testing.T) {
|
|||
},
|
||||
Match: match.Match{
|
||||
Vulnerability: vulnerability.Vulnerability{
|
||||
PackageName: "activerecord",
|
||||
Constraint: version.MustGetConstraint("< 3.7.6", version.UnknownFormat),
|
||||
ID: "CVE-2014-fake-3",
|
||||
Namespace: "nvd:cpe",
|
||||
|
@ -969,9 +979,10 @@ func TestVulnerabilityMatcher_FindMatches(t *testing.T) {
|
|||
wantMatches: match.NewMatches(
|
||||
match.Match{
|
||||
Vulnerability: vulnerability.Vulnerability{
|
||||
Constraint: version.MustGetConstraint("< 3.7.6", version.UnknownFormat),
|
||||
ID: "GHSA-2014-fake-3",
|
||||
Namespace: "github:language:ruby",
|
||||
PackageName: "activerecord",
|
||||
Constraint: version.MustGetConstraint("< 3.7.6", version.UnknownFormat),
|
||||
ID: "GHSA-2014-fake-3",
|
||||
Namespace: "github:language:ruby",
|
||||
RelatedVulnerabilities: []vulnerability.Reference{
|
||||
{
|
||||
ID: "CVE-2014-fake-3",
|
||||
|
@ -1010,9 +1021,10 @@ func TestVulnerabilityMatcher_FindMatches(t *testing.T) {
|
|||
},
|
||||
Match: match.Match{
|
||||
Vulnerability: vulnerability.Vulnerability{
|
||||
Constraint: version.MustGetConstraint("< 3.7.6", version.UnknownFormat),
|
||||
ID: "CVE-2014-fake-3",
|
||||
Namespace: "nvd:cpe",
|
||||
PackageName: "activerecord",
|
||||
Constraint: version.MustGetConstraint("< 3.7.6", version.UnknownFormat),
|
||||
ID: "CVE-2014-fake-3",
|
||||
Namespace: "nvd:cpe",
|
||||
CPEs: []cpe.CPE{
|
||||
mustCPE("cpe:2.3:*:activerecord:activerecord:*:*:*:*:*:rails:*:*"),
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue