Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(kevc): add exploitables in vulnerabilities #217

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions pkg/db/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ type Operation interface {
PutAdvisoryDetail(tx *bolt.Tx, vulnerabilityID, pkgName string, nestedBktNames []string, advisory interface{}) (err error)
DeleteAdvisoryDetailBucket() error

PutVulnerabilityExploitable(tx *bolt.Tx, cveID string, source types.SourceID, exploitable types.VulnerabilityExploitable) error
GetVulnerabilityExploitable(cveID string) (map[types.SourceID]types.VulnerabilityExploitable, error)
DeleteVulnerabilityExploitableBucket() error

PutDataSource(tx *bolt.Tx, bktName string, source types.DataSource) (err error)

// For Red Hat
Expand Down
46 changes: 46 additions & 0 deletions pkg/db/vulnerability_exploitable.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package db

import (
"encoding/json"

"github.com/aquasecurity/trivy-db/pkg/types"

bolt "go.etcd.io/bbolt"
"golang.org/x/xerrors"
)

const (
vulnerabilityExploitableBucket = "vulnerability-exploitable"
)

func (dbc Config) PutVulnerabilityExploitable(tx *bolt.Tx, cveID string, source types.SourceID, exploitable types.VulnerabilityExploitable) error {
if err := dbc.put(tx, []string{vulnerabilityExploitableBucket, cveID}, string(source), exploitable); err != nil {
return xerrors.Errorf("failed to put vulnerability exploitable: %w", err)
}
return nil
}

func (dbc Config) GetVulnerabilityExploitable(cveID string) (map[types.SourceID]types.VulnerabilityExploitable, error) {
values, err := dbc.forEach([]string{vulnerabilityExploitableBucket, cveID})
if err != nil {
return nil, xerrors.Errorf("error in Exploitable bucket get: %w", err)
}
if len(values) == 0 {
return nil, nil
}

exploitables := map[types.SourceID]types.VulnerabilityExploitable{}

for source, value := range values {
var exploitable types.VulnerabilityExploitable
if err = json.Unmarshal(value.Content, &exploitable); err != nil {
return nil, xerrors.Errorf("failed to unmarshal Vulnerability Exploitable JSON: %w", err)
}
exploitables[types.SourceID(source)] = exploitable
}
return exploitables, nil
}

func (dbc Config) DeleteVulnerabilityExploitableBucket() error {
return dbc.deleteBucket(vulnerabilityExploitableBucket)
}
13 changes: 13 additions & 0 deletions pkg/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ type Severity int

type VendorSeverity map[SourceID]Severity

type Exploitable map[SourceID]VulnerabilityExploitable

type CVSS struct {
V2Vector string `json:"V2Vector,omitempty"`
V3Vector string `json:"V3Vector,omitempty"`
Expand Down Expand Up @@ -136,9 +138,20 @@ type Vulnerability struct {
PublishedDate *time.Time `json:",omitempty"` // Take from NVD
LastModifiedDate *time.Time `json:",omitempty"` // Take from NVD

Exploitables map[SourceID]VulnerabilityExploitable

// Custom is basically for extensibility and is not supposed to be used in OSS
Custom interface{} `json:",omitempty"`
}

// VulnerabilityExploitable represents Exploit Database
type VulnerabilityExploitable struct {
DataSource *DataSource `json:",omitempty"`
DateAdded *time.Time
Description string
RequiredAction string
DueDate *time.Time
}

// Ecosystem represents language-specific ecosystem
type Ecosystem string
7 changes: 6 additions & 1 deletion pkg/vulndb/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@ func (t TrivyDB) optimize() error {
return nil
}

vuln := t.vulnClient.Normalize(details)
exploitables := t.vulnClient.GetExploitables(cveID)
vuln := t.vulnClient.Normalize(details, exploitables)
if err := t.dbc.PutVulnerability(tx, cveID, vuln); err != nil {
return xerrors.Errorf("failed to put vulnerability: %w", err)
}
Expand Down Expand Up @@ -169,5 +170,9 @@ func (t TrivyDB) cleanup() error {
return xerrors.Errorf("failed to delete advisory detail bucket: %w", err)
}

if err := t.dbc.DeleteVulnerabilityExploitableBucket(); err != nil {
return xerrors.Errorf("failed to delete vulnerability exploitable bucket: %w", err)
}

return nil
}
23 changes: 18 additions & 5 deletions pkg/vulndb/db_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package vulndb_test

import (
"encoding/json"
"github.com/aquasecurity/trivy-db/pkg/utils"
"os"
"path/filepath"
"strings"
Expand Down Expand Up @@ -121,9 +122,6 @@ func TestTrivyDB_Insert(t *testing.T) {
}

func TestTrivyDB_Build(t *testing.T) {
modified := time.Date(2020, 8, 24, 17, 37, 0, 0, time.UTC)
published := time.Date(2019, 4, 7, 0, 29, 0, 0, time.UTC)

type wantKV struct {
key []string
value interface{}
Expand All @@ -140,6 +138,7 @@ func TestTrivyDB_Build(t *testing.T) {
"testdata/fixtures/happy/vulnid.yaml",
"testdata/fixtures/happy/vulnerability-detail.yaml",
"testdata/fixtures/happy/advisory-detail.yaml",
"testdata/fixtures/happy/vulnerability-exploitable.yaml",
},
wantValues: []wantKV{
{
Expand All @@ -158,8 +157,21 @@ func TestTrivyDB_Build(t *testing.T) {
vulnerability.NVD: types.SeverityHigh,
vulnerability.RedHat: types.SeverityCritical,
},
PublishedDate: &published,
LastModifiedDate: &modified,
Exploitables: map[types.SourceID]types.VulnerabilityExploitable{
vulnerability.KnownExploitedVulnerabilitiesCatalog: {
DataSource: &types.DataSource{
ID: vulnerability.KnownExploitedVulnerabilitiesCatalog,
Name: "Known Exploited Vulnerabilities Catalog",
URL: "https://www.cisa.gov/known-exploited-vulnerabilities-catalog",
},
Description: "In Pallets Jinja before 2.10.1, str.format_map allows a sandbox escape.",
RequiredAction: "Apply updates per vendor instructions.",
DateAdded: utils.MustTimeParse("2019-04-07T00:00:00Z"),
DueDate: utils.MustTimeParse("2019-04-08T00:00:00Z"),
},
},
PublishedDate: utils.MustTimeParse("2019-04-07T00:29:00Z"),
LastModifiedDate: utils.MustTimeParse("2020-08-24T17:37:00Z"),
},
},
},
Expand Down Expand Up @@ -208,6 +220,7 @@ func TestTrivyDB_Build(t *testing.T) {
dbtest.NoBucket(t, dbPath, []string{"advisory-detail"})
dbtest.NoBucket(t, dbPath, []string{"vulnerability-detail"})
dbtest.NoBucket(t, dbPath, []string{"vulnerability-id"})
dbtest.NoBucket(t, dbPath, []string{"vulnerability-exploitable"})
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
- bucket: vulnerability-exploitable
pairs:
- bucket: CVE-2019-10906
pairs:
- key: known-exploited-vulnerabilities-catalog
value:
DataSource:
ID: known-exploited-vulnerabilities-catalog
Name: Known Exploited Vulnerabilities Catalog
URL: https://www.cisa.gov/known-exploited-vulnerabilities-catalog
Description: In Pallets Jinja before 2.10.1, str.format_map allows a sandbox escape.
RequiredAction: Apply updates per vendor instructions.
DateAdded: 2019-04-07T00:00:00Z
DueDate: 2019-04-08T00:00:00Z
139 changes: 139 additions & 0 deletions pkg/vulnsrc/kevc/kevc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package kevc

import (
"encoding/json"
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy-db/pkg/utils"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
bolt "go.etcd.io/bbolt"
"golang.org/x/xerrors"
"io"
"path/filepath"
"time"
)

var (
kevcDir = filepath.Join("kevc")
source = types.DataSource{
ID: vulnerability.KnownExploitedVulnerabilitiesCatalog,
Name: "Known Exploited Vulnerabilities Catalog",
URL: "https://www.cisa.gov/known-exploited-vulnerabilities-catalog",
}
)

const (
platformName = "Known Exploited Vulnerabilities Catalog"
DateFormat = "2006-01-02"
)

type Exploitable struct {
CveID string
DateAdded Time
Description string `json:"shortDescription"`
RequiredAction string
DueDate Time
}

type VulnSrc struct {
dbc db.Operation
}

func NewVulnSrc() VulnSrc {
return VulnSrc{
dbc: db.Config{},
}
}

func (vs VulnSrc) Name() types.SourceID {
return source.ID
}

func (vs VulnSrc) Update(dir string) error {
rootDir := filepath.Join(dir, "vuln-list", kevcDir)

var exploitables []Exploitable
err := utils.FileWalk(rootDir, func(r io.Reader, path string) error {
var e Exploitable
if err := json.NewDecoder(r).Decode(&e); err != nil {
return xerrors.Errorf("failed to decode json: %w", err)
}
exploitables = append(exploitables, e)
return nil
})

if err != nil {
return xerrors.Errorf("failed to walk file: %w", err)
}
if err := vs.save(exploitables); err != nil {
return xerrors.Errorf("failed to save exploitable: %w", err)
}

return nil
}

func (vs VulnSrc) save(exploitables []Exploitable) error {
err := vs.dbc.BatchUpdate(func(tx *bolt.Tx) error {
if err := vs.dbc.PutDataSource(tx, platformName, source); err != nil {
return xerrors.Errorf("failed to put data source: %w", err)
}

if err := vs.commit(tx, exploitables); err != nil {
return xerrors.Errorf("")
}
return nil
})
if err != nil {
return xerrors.Errorf("error in db batch update: %w", err)
}

return nil
}

func (vs VulnSrc) commit(tx *bolt.Tx, exploitables []Exploitable) error {
for _, exploitable := range exploitables {
e := types.VulnerabilityExploitable{
DataSource: &source,
DateAdded: &exploitable.DateAdded.Time,
Description: exploitable.Description,
RequiredAction: exploitable.RequiredAction,
DueDate: &exploitable.DueDate.Time,
}

if err := vs.dbc.PutVulnerabilityID(tx, exploitable.CveID); err != nil {
return xerrors.Errorf("failed to put Known Exploited Vulnerabilities ID: %w", err)
}

if err := vs.dbc.PutVulnerabilityExploitable(tx, exploitable.CveID, source.ID, e); err != nil {
return xerrors.Errorf("failed to save Known Exploited vulnerability Catalog: %w", err)
}
}
return nil
}

func (vs VulnSrc) Get(cveID string) (map[types.SourceID]types.VulnerabilityExploitable, error) {
exploitables, err := vs.dbc.GetVulnerabilityExploitable(cveID)
if err != nil {
return nil, xerrors.Errorf("failed to get Known Exploited Vulnerabilities Catalog: %w", err)
}
return exploitables, nil
}

type Time struct {
time.Time
}

func (date *Time) UnmarshalJSON(b []byte) error {
if string(b) == "null" {
date.Time = time.Time{}
return nil
}

var err error
date.Time, err = time.Parse(`"`+DateFormat+`"`, string(b))
if _, ok := err.(*time.ParseError); !ok {
return err
}
date.Time, err = time.Parse(`"`+time.RFC3339+`"`, string(b))
return err
}
64 changes: 64 additions & 0 deletions pkg/vulnsrc/kevc/kevc_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package kevc_test

import (
"github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy-db/pkg/utils"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/kevc"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
"github.com/aquasecurity/trivy-db/pkg/vulnsrctest"
"path/filepath"
"testing"
)

func TestVulnSrc_Update(t *testing.T) {
tests := []struct {
name string
dir string
wantValues []vulnsrctest.WantValues
wantErr string
}{
{
name: "happy path",
dir: filepath.Join("testdata", "happy"),
wantValues: []vulnsrctest.WantValues{
{
Key: []string{"data-source", "Known Exploited Vulnerabilities Catalog"},
Value: types.DataSource{
ID: vulnerability.KnownExploitedVulnerabilitiesCatalog,
Name: "Known Exploited Vulnerabilities Catalog",
URL: "https://www.cisa.gov/known-exploited-vulnerabilities-catalog",
},
},
{
Key: []string{"vulnerability-exploitable", "CVE-2022-0609", "known-exploited-vulnerabilities-catalog"},
Value: types.VulnerabilityExploitable{
DataSource: &types.DataSource{
ID: vulnerability.KnownExploitedVulnerabilitiesCatalog,
Name: "Known Exploited Vulnerabilities Catalog",
URL: "https://www.cisa.gov/known-exploited-vulnerabilities-catalog",
},
Description: "The vulnerability exists due to a use-after-free error within the Animation component in Google Chrome.",
RequiredAction: "Apply updates per vendor instructions.",
DateAdded: utils.MustTimeParse("2022-02-15T00:00:00Z"),
DueDate: utils.MustTimeParse("2022-03-01T00:00:00Z"),
},
},
},
},
{
name: "sad path",
dir: filepath.Join("testdata", "sad"),
wantErr: "failed to decode json",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
vs := kevc.NewVulnSrc()
vulnsrctest.TestUpdate(t, vs, vulnsrctest.TestUpdateArgs{
Dir: tt.dir,
WantValues: tt.wantValues,
WantErr: tt.wantErr,
})
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"cveID": "CVE-2022-0609",
"vendorProject": "Google",
"product": "Chrome",
"vulnerabilityName": "Google Chrome Use-After-Free Vulnerability",
"dateAdded": "2022-02-15",
"shortDescription": "The vulnerability exists due to a use-after-free error within the Animation component in Google Chrome.",
"requiredAction": "Apply updates per vendor instructions.",
"dueDate": "2022-03-01"
}
Loading