diff --git a/docs/01-Using-Fleet/03-REST-API.md b/docs/01-Using-Fleet/03-REST-API.md index c70298b8c6..217a1e3039 100644 --- a/docs/01-Using-Fleet/03-REST-API.md +++ b/docs/01-Using-Fleet/03-REST-API.md @@ -544,8 +544,21 @@ If `additional_info_filters` is not specified, no `additional` information will "team_id": null, "team_name": null, "pack_stats": null, - }, - ] + } + ], + "software": { + "id": 42, + "name": "app", + "version": "1.0.0", + "source": "rpm_packages", + "generated_cpe": "cpe:2.3:a:vendor:product:*:*:*", + "vulnerabilities": [ + { + "cve": "CVE-123-123-123", + "details_link": "https://link.to.cve" + } + ] + } } ``` diff --git a/server/datastore/mysql/software.go b/server/datastore/mysql/software.go index 199bb6c7da..3eeb50116a 100644 --- a/server/datastore/mysql/software.go +++ b/server/datastore/mysql/software.go @@ -368,5 +368,35 @@ func (d *Datastore) SoftwareByID(ctx context.Context, id uint) (*fleet.Software, if err != nil { return nil, errors.Wrap(err, "software by id") } + + query := ` + SELECT DISTINCT scv.cve + FROM software s + JOIN software_cpe scp ON (s.id=scp.software_id) + JOIN software_cve scv ON (scp.id=scv.cpe_id) + WHERE s.id=? + ` + + rows, err := d.reader.QueryxContext(ctx, query, id) + if err != nil { + return nil, errors.Wrap(err, "load software cves") + } + defer rows.Close() + + for rows.Next() { + var cve string + if err := rows.Scan(&cve); err != nil { + return nil, errors.Wrap(err, "scanning cve") + } + + software.Vulnerabilities = append(software.Vulnerabilities, fleet.SoftwareCVE{ + CVE: cve, + DetailsLink: fmt.Sprintf("https://nvd.nist.gov/vuln/detail/%s", cve), + }) + } + if err := rows.Err(); err != nil { + return nil, errors.Wrap(err, "error iterating through cve rows") + } + return &software, nil } diff --git a/server/datastore/mysql/software_test.go b/server/datastore/mysql/software_test.go index 6bbfe6fe29..88dbba57c0 100644 --- a/server/datastore/mysql/software_test.go +++ b/server/datastore/mysql/software_test.go @@ -263,6 +263,11 @@ func testSoftwareLoadVulnerabilities(t *testing.T, ds *Datastore) { require.NoError(t, ds.LoadHostSoftware(context.Background(), host)) + softByID, err := ds.SoftwareByID(context.Background(), host.HostSoftware.Software[0].ID) + require.NoError(t, err) + require.NotNil(t, softByID) + require.Len(t, softByID.Vulnerabilities, 2) + assert.Equal(t, "somecpe", host.Software[0].GenerateCPE) require.Len(t, host.Software[0].Vulnerabilities, 2) assert.Equal(t, "cve-123-123-132", host.Software[0].Vulnerabilities[0].CVE)