mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 00:45:19 +00:00
Add Description text to CVE Metadata (#13856)
This commit is contained in:
parent
741ace0515
commit
5bc6d30aa8
1
changes/9835-cve-descriptions
Normal file
1
changes/9835-cve-descriptions
Normal file
@ -0,0 +1 @@
|
||||
- CVE descriptions added to the /fleet/software API
|
@ -89,8 +89,8 @@ $ docker-compose -f docker-compose.yml -f docker-compose-redis-cluster.yml up
|
||||
|
||||
To run all Go unit tests, run the following:
|
||||
|
||||
```
|
||||
REDIS_TEST=1 MYSQL_TEST=1 MINIO_STORAGE_TEST=1 SAML_IDP_TEST=1 make test-go
|
||||
```bash
|
||||
REDIS_TEST=1 MYSQL_TEST=1 MINIO_STORAGE_TEST=1 SAML_IDP_TEST=1 NETWORK_TEST=1 make test-go
|
||||
```
|
||||
|
||||
### Go linters
|
||||
|
@ -0,0 +1,25 @@
|
||||
package tables
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func init() {
|
||||
MigrationClient.AddMigration(Up_20230912101759, Down_20230912101759)
|
||||
}
|
||||
|
||||
func Up_20230912101759(tx *sql.Tx) error {
|
||||
stmt := `
|
||||
ALTER TABLE cve_meta
|
||||
ADD COLUMN description TEXT COLLATE utf8mb4_unicode_ci DEFAULT NULL;
|
||||
`
|
||||
if _, err := tx.Exec(stmt); err != nil {
|
||||
return fmt.Errorf("add description to cve_meta: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Down_20230912101759(tx *sql.Tx) error {
|
||||
return nil
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package tables
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestUp_20230912101759(t *testing.T) {
|
||||
db := applyUpToPrev(t)
|
||||
insertStmt := `
|
||||
INSERT INTO cve_meta
|
||||
(cve)
|
||||
VALUES
|
||||
(?)
|
||||
`
|
||||
cveVal := "CVE-2010-3262"
|
||||
execNoErr(t, db, insertStmt, cveVal)
|
||||
|
||||
applyNext(t, db)
|
||||
|
||||
// retrieve the stored value
|
||||
var cveMeta struct {
|
||||
CVE string `db:"cve"`
|
||||
Description *string `db:"description"`
|
||||
}
|
||||
|
||||
err := db.Get(&cveMeta, "SELECT cve, description FROM cve_meta WHERE cve = ?", cveVal)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, cveVal, cveMeta.CVE)
|
||||
require.Nil(t, cveMeta.Description)
|
||||
|
||||
insertStmt = `
|
||||
INSERT INTO cve_meta
|
||||
(cve, description)
|
||||
VALUES
|
||||
(?, ?)
|
||||
`
|
||||
cveVal = "CVE-2010-3263"
|
||||
descVal := "Cross-site scripting (XSS) vulnerability in setup/frames/index.inc.php in the setup script in phpMyAdmin 3.x before 3.3.7 allows remote attackers to inject arbitrary web script or HTML via a server name."
|
||||
execNoErr(t, db, insertStmt, cveVal, descVal)
|
||||
err = db.Get(&cveMeta, "SELECT cve, description FROM cve_meta WHERE cve = ?", cveVal)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, cveVal, cveMeta.CVE)
|
||||
require.Equal(t, &descVal, cveMeta.Description)
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -587,6 +587,7 @@ func listSoftwareDB(
|
||||
cve.EPSSProbability = &result.EPSSProbability
|
||||
cve.CISAKnownExploit = &result.CISAKnownExploit
|
||||
cve.CVEPublished = &result.CVEPublished
|
||||
cve.Description = &result.Description
|
||||
}
|
||||
softwares[idx].Vulnerabilities = append(softwares[idx].Vulnerabilities, cve)
|
||||
}
|
||||
@ -603,6 +604,7 @@ type softwareCVE struct {
|
||||
EPSSProbability *float64 `db:"epss_probability"`
|
||||
CISAKnownExploit *bool `db:"cisa_known_exploit"`
|
||||
CVEPublished *time.Time `db:"cve_published"`
|
||||
Description *string `db:"description"`
|
||||
}
|
||||
|
||||
func selectSoftwareSQL(opts fleet.SoftwareListOptions) (string, []interface{}, error) {
|
||||
@ -696,6 +698,7 @@ func selectSoftwareSQL(opts fleet.SoftwareListOptions) (string, []interface{}, e
|
||||
goqu.MAX("c.epss_probability").As("epss_probability"), // for ordering
|
||||
goqu.MAX("c.cisa_known_exploit").As("cisa_known_exploit"), // for ordering
|
||||
goqu.MAX("c.published").As("cve_published"), // for ordering
|
||||
goqu.MAX("c.description").As("description"), // for ordering
|
||||
)
|
||||
}
|
||||
|
||||
@ -763,6 +766,7 @@ func selectSoftwareSQL(opts fleet.SoftwareListOptions) (string, []interface{}, e
|
||||
"c.cvss_score",
|
||||
"c.epss_probability",
|
||||
"c.cisa_known_exploit",
|
||||
"c.description",
|
||||
goqu.I("c.published").As("cve_published"),
|
||||
)
|
||||
}
|
||||
@ -1062,6 +1066,7 @@ func (ds *Datastore) SoftwareByID(ctx context.Context, id uint, includeCVEScores
|
||||
"c.cvss_score",
|
||||
"c.epss_probability",
|
||||
"c.cisa_known_exploit",
|
||||
"c.description",
|
||||
goqu.I("c.published").As("cve_published"),
|
||||
)
|
||||
}
|
||||
@ -1356,13 +1361,14 @@ func (ds *Datastore) HostsByCVE(ctx context.Context, cve string) ([]fleet.HostVu
|
||||
|
||||
func (ds *Datastore) InsertCVEMeta(ctx context.Context, cveMeta []fleet.CVEMeta) error {
|
||||
query := `
|
||||
INSERT INTO cve_meta (cve, cvss_score, epss_probability, cisa_known_exploit, published)
|
||||
INSERT INTO cve_meta (cve, cvss_score, epss_probability, cisa_known_exploit, published, description)
|
||||
VALUES %s
|
||||
ON DUPLICATE KEY UPDATE
|
||||
cvss_score = VALUES(cvss_score),
|
||||
epss_probability = VALUES(epss_probability),
|
||||
cisa_known_exploit = VALUES(cisa_known_exploit),
|
||||
published = VALUES(published)
|
||||
published = VALUES(published),
|
||||
description = VALUES(description)
|
||||
`
|
||||
|
||||
batchSize := 500
|
||||
@ -1374,10 +1380,10 @@ ON DUPLICATE KEY UPDATE
|
||||
|
||||
batch := cveMeta[i:end]
|
||||
|
||||
valuesFrag := strings.TrimSuffix(strings.Repeat("(?, ?, ?, ?, ?), ", len(batch)), ", ")
|
||||
valuesFrag := strings.TrimSuffix(strings.Repeat("(?, ?, ?, ?, ?, ?), ", len(batch)), ", ")
|
||||
var args []interface{}
|
||||
for _, meta := range batch {
|
||||
args = append(args, meta.CVE, meta.CVSSScore, meta.EPSSProbability, meta.CISAKnownExploit, meta.Published)
|
||||
args = append(args, meta.CVE, meta.CVSSScore, meta.EPSSProbability, meta.CISAKnownExploit, meta.Published, meta.Description)
|
||||
}
|
||||
|
||||
query := fmt.Sprintf(query, valuesFrag)
|
||||
@ -1514,6 +1520,7 @@ func (ds *Datastore) ListCVEs(ctx context.Context, maxAge time.Duration) ([]flee
|
||||
goqu.C("epss_probability"),
|
||||
goqu.C("cisa_known_exploit"),
|
||||
goqu.C("published"),
|
||||
goqu.C("description"),
|
||||
).
|
||||
Where(goqu.C("published").Gte(maxAgeDate))
|
||||
|
||||
|
@ -566,6 +566,7 @@ func testSoftwareList(t *testing.T, ds *Datastore) {
|
||||
EPSSProbability: ptr.Float64(0.01),
|
||||
CISAKnownExploit: ptr.Bool(false),
|
||||
Published: ptr.Time(now.Add(-2 * time.Hour)),
|
||||
Description: "this is a description for CVE-2022-0001",
|
||||
},
|
||||
{
|
||||
CVE: "CVE-2022-0002",
|
||||
@ -573,6 +574,7 @@ func testSoftwareList(t *testing.T, ds *Datastore) {
|
||||
EPSSProbability: ptr.Float64(0.99),
|
||||
CISAKnownExploit: ptr.Bool(false),
|
||||
Published: ptr.Time(now),
|
||||
Description: "this is a description for CVE-2022-0002",
|
||||
},
|
||||
{
|
||||
CVE: "CVE-2022-0003",
|
||||
@ -580,6 +582,7 @@ func testSoftwareList(t *testing.T, ds *Datastore) {
|
||||
EPSSProbability: ptr.Float64(0.98),
|
||||
CISAKnownExploit: ptr.Bool(true),
|
||||
Published: ptr.Time(now.Add(-1 * time.Hour)),
|
||||
Description: "this is a description for CVE-2022-0003",
|
||||
},
|
||||
}
|
||||
err = ds.InsertCVEMeta(context.Background(), cveMeta)
|
||||
@ -598,6 +601,7 @@ func testSoftwareList(t *testing.T, ds *Datastore) {
|
||||
EPSSProbability: ptr.Float64Ptr(0.01),
|
||||
CISAKnownExploit: ptr.BoolPtr(false),
|
||||
CVEPublished: ptr.TimePtr(now.Add(-2 * time.Hour)),
|
||||
Description: ptr.StringPtr("this is a description for CVE-2022-0001"),
|
||||
},
|
||||
{
|
||||
CVE: "CVE-2022-0002",
|
||||
@ -606,6 +610,7 @@ func testSoftwareList(t *testing.T, ds *Datastore) {
|
||||
EPSSProbability: ptr.Float64Ptr(0.99),
|
||||
CISAKnownExploit: ptr.BoolPtr(false),
|
||||
CVEPublished: ptr.TimePtr(now),
|
||||
Description: ptr.StringPtr("this is a description for CVE-2022-0002"),
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -625,6 +630,7 @@ func testSoftwareList(t *testing.T, ds *Datastore) {
|
||||
EPSSProbability: ptr.Float64Ptr(0.98),
|
||||
CISAKnownExploit: ptr.BoolPtr(true),
|
||||
CVEPublished: ptr.TimePtr(now.Add(-1 * time.Hour)),
|
||||
Description: ptr.StringPtr("this is a description for CVE-2022-0003"),
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -1815,10 +1821,10 @@ func testListCVEs(t *testing.T, ds *Datastore) {
|
||||
twoMonthsAgo := now.Add(-60 * 24 * time.Hour)
|
||||
|
||||
testCases := []fleet.CVEMeta{
|
||||
{CVE: "cve-1", Published: &threeDaysAgo},
|
||||
{CVE: "cve-2", Published: &twoWeeksAgo},
|
||||
{CVE: "cve-3", Published: &twoMonthsAgo},
|
||||
{CVE: "cve-4"},
|
||||
{CVE: "cve-1", Published: &threeDaysAgo, Description: "cve-1 description"},
|
||||
{CVE: "cve-2", Published: &twoWeeksAgo, Description: "cve-2 description"},
|
||||
{CVE: "cve-3", Published: &twoMonthsAgo}, // past maxAge
|
||||
{CVE: "cve-4"}, // no published date
|
||||
}
|
||||
|
||||
err := ds.InsertCVEMeta(ctx, testCases)
|
||||
@ -1827,12 +1833,12 @@ func testListCVEs(t *testing.T, ds *Datastore) {
|
||||
result, err := ds.ListCVEs(ctx, 30*24*time.Hour)
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := []string{"cve-1", "cve-2"}
|
||||
expected := []string{"cve-1", "cve-1 description", "cve-2", "cve-2 description"}
|
||||
var actual []string
|
||||
for _, r := range result {
|
||||
actual = append(actual, r.CVE)
|
||||
actual = append(actual, r.Description)
|
||||
}
|
||||
|
||||
require.ElementsMatch(t, expected, actual)
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@ type CVE struct {
|
||||
EPSSProbability **float64 `json:"epss_probability,omitempty" db:"epss_probability"`
|
||||
CISAKnownExploit **bool `json:"cisa_known_exploit,omitempty" db:"cisa_known_exploit"`
|
||||
CVEPublished **time.Time `json:"cve_published,omitempty" db:"cve_published"`
|
||||
Description **string `json:"cve_description,omitempty" db:"description"`
|
||||
}
|
||||
|
||||
type CVEMeta struct {
|
||||
@ -33,6 +34,8 @@ type CVEMeta struct {
|
||||
CISAKnownExploit *bool `db:"cisa_known_exploit"`
|
||||
// Published is when the cve was published according to NIST.score
|
||||
Published *time.Time `db:"published"`
|
||||
// CVE text description
|
||||
Description string `db:"description"`
|
||||
}
|
||||
|
||||
// SoftwareCPE represents an entry in the `software_cpe` table.
|
||||
|
@ -32,6 +32,11 @@ func BoolPtr(x bool) **bool {
|
||||
return &p
|
||||
}
|
||||
|
||||
func StringPtr(x string) **string {
|
||||
p := String(x)
|
||||
return &p
|
||||
}
|
||||
|
||||
// Time returns a pointer to the provided time.Time.
|
||||
func Time(x time.Time) *time.Time {
|
||||
return &x
|
||||
|
@ -2925,6 +2925,7 @@ func (s *integrationEnterpriseTestSuite) TestListSoftware() {
|
||||
EPSSProbability: ptr.Float64(0.5),
|
||||
CISAKnownExploit: ptr.Bool(true),
|
||||
Published: &now,
|
||||
Description: "a long description of the cve",
|
||||
}}))
|
||||
|
||||
require.NoError(t, s.ds.SyncHostsSoftware(ctx, time.Now().UTC()))
|
||||
@ -2953,6 +2954,7 @@ func (s *integrationEnterpriseTestSuite) TestListSoftware() {
|
||||
require.NotNil(t, barPayload.Vulnerabilities[0].EPSSProbability, ptr.Float64Ptr(0.5))
|
||||
require.NotNil(t, barPayload.Vulnerabilities[0].CISAKnownExploit, ptr.BoolPtr(true))
|
||||
require.Equal(t, barPayload.Vulnerabilities[0].CVEPublished, ptr.TimePtr(now))
|
||||
require.Equal(t, barPayload.Vulnerabilities[0].Description, ptr.StringPtr("a long description of the cve"))
|
||||
}
|
||||
|
||||
// TestGitOpsUserActions tests the permissions listed in ../../docs/Using-Fleet/Permissions.md.
|
||||
|
@ -16,8 +16,8 @@ import (
|
||||
"github.com/facebookincubator/nvdtools/providers/nvd"
|
||||
"github.com/facebookincubator/nvdtools/wfn"
|
||||
"github.com/fleetdm/fleet/v4/server/fleet"
|
||||
kitlog "github.com/go-kit/kit/log"
|
||||
"github.com/go-kit/kit/log/level"
|
||||
kitlog "github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
)
|
||||
|
||||
// DownloadNVDCVEFeed downloads the NVD CVE feed. Skips downloading if the cve feed has not changed since the last time.
|
||||
|
@ -210,7 +210,8 @@ func LoadCVEMeta(ctx context.Context, logger log.Logger, vulnPath string, ds fle
|
||||
schema := vuln.Schema()
|
||||
|
||||
meta := fleet.CVEMeta{
|
||||
CVE: cve,
|
||||
CVE: cve,
|
||||
Description: schema.CVE.Description.DescriptionData[0].Value,
|
||||
}
|
||||
|
||||
if schema.Impact.BaseMetricV3 != nil {
|
||||
|
@ -64,11 +64,21 @@ func TestLoadCVEMeta(t *testing.T) {
|
||||
require.Equal(t, float64(7.2), *meta.CVSSScore)
|
||||
require.Equal(t, float64(0.00885), *meta.EPSSProbability)
|
||||
require.Equal(t, false, *meta.CISAKnownExploit)
|
||||
require.Equal(
|
||||
t,
|
||||
"CSCMS Music Portal System v4.2 was discovered to contain a SQL injection vulnerability via the id parameter at /admin.php/pic/admin/lists/zhuan.",
|
||||
*&meta.Description,
|
||||
)
|
||||
|
||||
meta = metaMap["CVE-2022-22587"]
|
||||
require.Equal(t, float64(9.8), *meta.CVSSScore)
|
||||
require.Equal(t, float64(0.01843), *meta.EPSSProbability)
|
||||
require.Equal(t, true, *meta.CISAKnownExploit)
|
||||
require.Equal(
|
||||
t,
|
||||
"A memory corruption issue was addressed with improved input validation. This issue is fixed in iOS 15.3 and iPadOS 15.3, macOS Big Sur 11.6.3, macOS Monterey 12.2. A malicious application may be able to execute arbitrary code with kernel privileges. Apple is aware of a report that this issue may have been actively exploited..",
|
||||
*&meta.Description,
|
||||
)
|
||||
}
|
||||
|
||||
func TestDownloadCPETranslations(t *testing.T) {
|
||||
|
Loading…
Reference in New Issue
Block a user