Add visual studio extensions to software inventory (#17501)
#17003 - [X] Changes file added for user-visible changes in `changes/` or `orbit/changes/`. See [Changes files](https://fleetdm.com/docs/contributing/committing-changes#changes-files) for more information. - [X] Added support on fleet's osquery simulator `cmd/osquery-perf` for new osquery data ingestion features. - [x] Added/updated tests - [X] Manual QA for all new/changed functionality
This commit is contained in:
@@ -10,7 +10,7 @@ To test these changes locally, you can:
|
||||
2. host this file on a local web server
|
||||
|
||||
```bash
|
||||
go run ./tools/file-server 8082 ./server/vulnerabilities/nvd
|
||||
go run ./tools/file-server/main.go 8082 ./server/vulnerabilities/nvd/
|
||||
```
|
||||
|
||||
3. (re)launch your local fleet server with one of the following
|
||||
@@ -20,7 +20,6 @@ To test these changes locally, you can:
|
||||
vulnerabilities:
|
||||
cpe_translations_url: "http://localhost:8082/cpe_translations.json"
|
||||
```
|
||||
|
||||
|
||||
Environment method
|
||||
```bash
|
||||
@@ -28,7 +27,6 @@ To test these changes locally, you can:
|
||||
```
|
||||
|
||||
4. trigger a vulnerabilities scan
|
||||
|
||||
```bash
|
||||
fleetctl trigger --name vulnerabilities
|
||||
```
|
||||
|
||||
@@ -41,6 +41,8 @@ type CPEMatchingRule struct {
|
||||
CVEs map[string]struct{}
|
||||
// IgnoreAll will cause all CPEs to not match hence ignoring a CVE.
|
||||
IgnoreAll bool
|
||||
// IgnoreIf is a function that can determine if a CPE matching rule should be ignored or not.
|
||||
IgnoreIf func(cpeMeta *wfn.Attributes) bool
|
||||
}
|
||||
|
||||
// CPEMatches returns true if the provided CPE matches the rule.
|
||||
@@ -53,6 +55,10 @@ func (rule CPEMatchingRule) CPEMatches(cpeMeta *wfn.Attributes) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
if rule.IgnoreIf != nil && rule.IgnoreIf(cpeMeta) {
|
||||
return false
|
||||
}
|
||||
|
||||
ver, err := semver.NewVersion(wfn.StripSlashes(cpeMeta.Version))
|
||||
if err != nil {
|
||||
return false
|
||||
|
||||
@@ -2,6 +2,8 @@ package nvd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/facebookincubator/nvdtools/wfn"
|
||||
)
|
||||
|
||||
type CPEMatchingRules []CPEMatchingRule
|
||||
@@ -181,6 +183,20 @@ func GetKnownNVDBugRules() (CPEMatchingRules, error) {
|
||||
},
|
||||
},
|
||||
},
|
||||
// These vulnerabilities in the MongoDB client incorrectly match
|
||||
// the VS Code extension.
|
||||
CPEMatchingRule{
|
||||
CVEs: map[string]struct{}{
|
||||
"CVE-2012-6619": {},
|
||||
"CVE-2013-1892": {},
|
||||
"CVE-2013-2132": {},
|
||||
"CVE-2015-1609": {},
|
||||
"CVE-2016-6494": {},
|
||||
},
|
||||
IgnoreIf: func(cpeMeta *wfn.Attributes) bool {
|
||||
return cpeMeta.TargetSW == "visual_studio_code"
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, rule := range rules {
|
||||
|
||||
@@ -466,8 +466,6 @@ func TestLegacyCPEDB(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCPEFromSoftwareIntegration(t *testing.T) {
|
||||
nettest.Run(t)
|
||||
|
||||
testCases := []struct {
|
||||
software fleet.Software
|
||||
cpe string
|
||||
@@ -1334,6 +1332,245 @@ func TestCPEFromSoftwareIntegration(t *testing.T) {
|
||||
},
|
||||
cpe: "cpe:2.3:a:jetbrains:pycharm:2022.1:*:*:*:*:macos:*:*",
|
||||
},
|
||||
{
|
||||
software: fleet.Software{
|
||||
Name: "eamodio.gitlens",
|
||||
Source: "vscode_extensions",
|
||||
Version: "14.9.0",
|
||||
Vendor: "GitKraken",
|
||||
},
|
||||
cpe: "cpe:2.3:a:gitkraken:gitlens:14.9.0:*:*:*:*:visual_studio_code:*:*",
|
||||
},
|
||||
{
|
||||
software: fleet.Software{
|
||||
Name: "ms-python.python",
|
||||
Source: "vscode_extensions",
|
||||
Version: "2024.2.1",
|
||||
Vendor: "Microsoft",
|
||||
},
|
||||
cpe: "cpe:2.3:a:microsoft:python_extension:2024.2.1:*:*:*:*:visual_studio_code:*:*",
|
||||
},
|
||||
{
|
||||
software: fleet.Software{
|
||||
Name: "ms-toolsai.jupyter",
|
||||
Source: "vscode_extensions",
|
||||
Version: "2024.2.0",
|
||||
Vendor: "Microsoft",
|
||||
},
|
||||
cpe: "cpe:2.3:a:microsoft:jupyter:2024.2.0:*:*:*:*:visual_studio_code:*:*",
|
||||
},
|
||||
{
|
||||
software: fleet.Software{
|
||||
Name: "ms-vsliveshare.vsliveshare",
|
||||
Source: "vscode_extensions",
|
||||
Version: "1.0.5918",
|
||||
Vendor: "Microsoft",
|
||||
},
|
||||
cpe: "cpe:2.3:a:microsoft:visual_studio_live_share:1.0.5918:*:*:*:*:visual_studio_code:*:*",
|
||||
},
|
||||
{
|
||||
software: fleet.Software{
|
||||
Name: "dbaeumer.vscode-eslint",
|
||||
Source: "vscode_extensions",
|
||||
Version: "2.4.4",
|
||||
Vendor: "Microsoft",
|
||||
},
|
||||
cpe: "cpe:2.3:a:microsoft:visual_studio_code_eslint_extension:2.4.4:*:*:*:*:visual_studio_code:*:*",
|
||||
},
|
||||
{
|
||||
software: fleet.Software{
|
||||
Name: "vscjava.vscode-maven",
|
||||
Source: "vscode_extensions",
|
||||
Version: "0.44.0",
|
||||
Vendor: "Microsoft",
|
||||
},
|
||||
cpe: "cpe:2.3:a:microsoft:vscode-maven:0.44.0:*:*:*:*:visual_studio_code:*:*",
|
||||
},
|
||||
{
|
||||
software: fleet.Software{
|
||||
Name: "ms-vscode.powershell",
|
||||
Source: "vscode_extensions",
|
||||
Version: "2024.0.0",
|
||||
Vendor: "Microsoft",
|
||||
},
|
||||
cpe: "cpe:2.3:a:microsoft:powershell_extension:2024.0.0:*:*:*:*:visual_studio_code:*:*",
|
||||
},
|
||||
{
|
||||
software: fleet.Software{
|
||||
Name: "ms-vscode-remote.vscode-remote-extensionpack",
|
||||
Source: "vscode_extensions",
|
||||
Version: "0.25.0",
|
||||
Vendor: "Microsoft",
|
||||
},
|
||||
cpe: "cpe:2.3:a:microsoft:remote_development:0.25.0:*:*:*:*:visual_studio_code:*:*",
|
||||
},
|
||||
{
|
||||
software: fleet.Software{
|
||||
Name: "vknabel.vscode-swiftlint",
|
||||
Source: "vscode_extensions",
|
||||
Version: "1.8.3",
|
||||
Vendor: "vknabel",
|
||||
},
|
||||
cpe: "cpe:2.3:a:swiftlint_project:swiftlint:1.8.3:*:*:*:*:visual_studio_code:*:*",
|
||||
},
|
||||
{
|
||||
software: fleet.Software{
|
||||
Name: "vknabel.vscode-swiftformat",
|
||||
Source: "vscode_extensions",
|
||||
Version: "1.6.7",
|
||||
Vendor: "vknabel",
|
||||
},
|
||||
cpe: "cpe:2.3:a:swiftformat_project:swiftformat:1.6.7:*:*:*:*:visual_studio_code:*:*",
|
||||
},
|
||||
{
|
||||
software: fleet.Software{
|
||||
Name: "jbenden.c-cpp-flylint",
|
||||
Source: "vscode_extensions",
|
||||
Version: "1.14.0",
|
||||
Vendor: "Joseph Benden",
|
||||
},
|
||||
cpe: `cpe:2.3:a:c\/c\+\+_advanced_lint_project:c\/c\+\+_advanced_lint:1.14.0:*:*:*:*:visual_studio_code:*:*`,
|
||||
},
|
||||
{
|
||||
software: fleet.Software{
|
||||
Name: "stripe.vscode-stripe",
|
||||
Source: "vscode_extensions",
|
||||
Version: "2.0.14",
|
||||
Vendor: "Stripe",
|
||||
},
|
||||
cpe: `cpe:2.3:a:stripe:stripe:2.0.14:*:*:*:*:visual_studio_code:*:*`,
|
||||
},
|
||||
{
|
||||
software: fleet.Software{
|
||||
Name: "vscodevim.vim",
|
||||
Source: "vscode_extensions",
|
||||
Version: "1.27.2",
|
||||
Vendor: "vscodevim",
|
||||
},
|
||||
cpe: `cpe:2.3:a:vim_project:vim:1.27.2:*:*:*:*:visual_studio_code:*:*`,
|
||||
},
|
||||
{
|
||||
software: fleet.Software{
|
||||
Name: "svelte.svelte-vscode",
|
||||
Source: "vscode_extensions",
|
||||
Version: "108.3.1",
|
||||
Vendor: "Svelte",
|
||||
},
|
||||
cpe: `cpe:2.3:a:svelte:svelte:108.3.1:*:*:*:*:visual_studio_code:*:*`,
|
||||
},
|
||||
{
|
||||
software: fleet.Software{
|
||||
Name: "lextudio.restructuredtext",
|
||||
Source: "vscode_extensions",
|
||||
Version: "189.3.0",
|
||||
Vendor: "LeXtudio Inc.",
|
||||
},
|
||||
cpe: `cpe:2.3:a:lextudio:restructuredtext:189.3.0:*:*:*:*:visual_studio_code:*:*`,
|
||||
},
|
||||
{
|
||||
software: fleet.Software{
|
||||
Name: "ms-vscode-remote.remote-containers",
|
||||
Source: "vscode_extensions",
|
||||
Version: "0.348.0",
|
||||
Vendor: "Microsoft",
|
||||
},
|
||||
cpe: `cpe:2.3:a:microsoft:remote:0.348.0:*:*:*:*:visual_studio_code:*:*`,
|
||||
},
|
||||
{
|
||||
software: fleet.Software{
|
||||
Name: "ms-kubernetes-tools.vscode-kubernetes-tools",
|
||||
Source: "vscode_extensions",
|
||||
Version: "0.348.0",
|
||||
Vendor: "Microsoft",
|
||||
},
|
||||
cpe: `cpe:2.3:a:microsoft:kubernetes_tools:0.348.0:*:*:*:*:visual_studio_code:*:*`,
|
||||
},
|
||||
{
|
||||
software: fleet.Software{
|
||||
Name: "ms-dotnettools.vscode-dotnet-sdk",
|
||||
Source: "vscode_extensions",
|
||||
Version: "0.8.0",
|
||||
Vendor: "Microsoft",
|
||||
},
|
||||
cpe: `cpe:2.3:a:microsoft:.net_education_bundle_sdk_install_tool:0.8.0:*:*:*:*:visual_studio_code:*:*`,
|
||||
},
|
||||
{
|
||||
software: fleet.Software{
|
||||
Name: "ms-dotnettools.vscode-dotnet-runtime",
|
||||
Source: "vscode_extensions",
|
||||
Version: "2.0.2",
|
||||
Vendor: "Microsoft",
|
||||
},
|
||||
cpe: `cpe:2.3:a:microsoft:.net_install_tool_for_extension_authors:2.0.2:*:*:*:*:visual_studio_code:*:*`,
|
||||
},
|
||||
{
|
||||
software: fleet.Software{
|
||||
Name: "ms-vscode-remote.remote-wsl",
|
||||
Source: "vscode_extensions",
|
||||
Version: "0.86.0",
|
||||
Vendor: "Microsoft",
|
||||
},
|
||||
cpe: `cpe:2.3:a:microsoft:windows_subsystem_for_linux:0.86.0:*:*:*:*:visual_studio_code:*:*`,
|
||||
},
|
||||
{
|
||||
software: fleet.Software{
|
||||
Name: "mongodb.mongodb-vscode",
|
||||
Source: "vscode_extensions",
|
||||
Version: "1.5.0",
|
||||
Vendor: "MongoDB",
|
||||
},
|
||||
cpe: `cpe:2.3:a:mongodb:mongodb:1.5.0:*:*:*:*:visual_studio_code:*:*`,
|
||||
},
|
||||
{
|
||||
software: fleet.Software{
|
||||
Name: "oracle.mysql-shell-for-vs-code",
|
||||
Source: "vscode_extensions",
|
||||
Version: "1.14.2",
|
||||
Vendor: "MongoDB",
|
||||
},
|
||||
cpe: `cpe:2.3:a:oracle:mysql_shell:1.14.2:*:*:*:*:visual_studio_code:*:*`,
|
||||
},
|
||||
{
|
||||
software: fleet.Software{
|
||||
Name: "snyk-security.snyk-vulnerability-scanner",
|
||||
Source: "vscode_extensions",
|
||||
Version: "2.3.6",
|
||||
Vendor: "Snyk",
|
||||
},
|
||||
cpe: `cpe:2.3:a:snyk:snyk_security:2.3.6:*:*:*:*:visual_studio_code:*:*`,
|
||||
},
|
||||
{
|
||||
software: fleet.Software{
|
||||
Name: "sourcegraph.cody-ai",
|
||||
Source: "vscode_extensions",
|
||||
Version: "1.8.0",
|
||||
Vendor: "Sourcegraph",
|
||||
},
|
||||
cpe: `cpe:2.3:a:sourcegraph:cody:1.8.0:*:*:*:*:visual_studio_code:*:*`,
|
||||
},
|
||||
// There are vulnerabilities for `cpe:2.3:a:redhat:vscode-xml:` in
|
||||
// NVD's database but there's no entry for `cpe:2.3:a:redhat:vscode-xml:0.26.1`
|
||||
// in NVD's CPE database.
|
||||
/*
|
||||
{
|
||||
software: fleet.Software{
|
||||
Name: "redhat.vscode-xml",
|
||||
Source: "vscode_extensions",
|
||||
Version: "0.26.1",
|
||||
Vendor: "Red Hat",
|
||||
},
|
||||
cpe: `cpe:2.3:a:redhat:vscode-xml:0.26.1:*:*:*:*:visual_studio_code:*:*`,
|
||||
},
|
||||
*/
|
||||
{
|
||||
software: fleet.Software{
|
||||
Name: "github.vscode-pull-request-github",
|
||||
Source: "vscode_extensions",
|
||||
Version: "0.82.0",
|
||||
Vendor: "GitHub",
|
||||
},
|
||||
cpe: `cpe:2.3:a:github:pull_requests_and_issues:0.82.0:*:*:*:*:visual_studio_code:*:*`,
|
||||
},
|
||||
{
|
||||
software: fleet.Software{
|
||||
Name: "Google Chrome Helper.app",
|
||||
@@ -1369,18 +1606,22 @@ func TestCPEFromSoftwareIntegration(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
tempDir := t.TempDir()
|
||||
// NVD_TEST_CPEDB_PATH can be used to speed up development (sync cpe.sqlite only once).
|
||||
dbPath := os.Getenv("NVD_TEST_CPEDB_PATH")
|
||||
if dbPath == "" {
|
||||
nettest.Run(t)
|
||||
tempDir := t.TempDir()
|
||||
err := DownloadCPEDBFromGithub(tempDir, "")
|
||||
require.NoError(t, err)
|
||||
dbPath = filepath.Join(tempDir, "cpe.sqlite")
|
||||
} else {
|
||||
require.FileExists(t, dbPath)
|
||||
t.Logf("Using %s as database file", dbPath)
|
||||
}
|
||||
|
||||
err := DownloadCPEDBFromGithub(tempDir, "")
|
||||
require.NoError(t, err)
|
||||
|
||||
dbPath := filepath.Join(tempDir, "cpe.sqlite")
|
||||
db, err := sqliteDB(dbPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = DownloadCPETranslationsFromGithub(tempDir, "")
|
||||
require.NoError(t, err)
|
||||
|
||||
cpeTranslationsPath := filepath.Join(".", cpeTranslationsFilename)
|
||||
cpeTranslations, err := loadCPETranslations(cpeTranslationsPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -140,6 +140,216 @@
|
||||
"vendor": ["jetbrains"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"software": {
|
||||
"name": ["ms-python.python"],
|
||||
"source": ["vscode_extensions"]
|
||||
},
|
||||
"filter": {
|
||||
"product": ["python_extension"],
|
||||
"vendor": ["microsoft"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"software": {
|
||||
"name": ["ms-vsliveshare.vsliveshare"],
|
||||
"source": ["vscode_extensions"]
|
||||
},
|
||||
"filter": {
|
||||
"product": ["visual_studio_live_share"],
|
||||
"vendor": ["microsoft"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"software": {
|
||||
"name": ["dbaeumer.vscode-eslint"],
|
||||
"source": ["vscode_extensions"]
|
||||
},
|
||||
"filter": {
|
||||
"product": ["visual_studio_code_eslint_extension"],
|
||||
"vendor": ["microsoft"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"software": {
|
||||
"name": ["ms-vscode.powershell"],
|
||||
"source": ["vscode_extensions"]
|
||||
},
|
||||
"filter": {
|
||||
"product": ["powershell_extension"],
|
||||
"vendor": ["microsoft"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"software": {
|
||||
"name": ["ms-vscode-remote.vscode-remote-extensionpack"],
|
||||
"source": ["vscode_extensions"]
|
||||
},
|
||||
"filter": {
|
||||
"product": ["remote_development"],
|
||||
"vendor": ["microsoft"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"software": {
|
||||
"name": ["vknabel.vscode-swiftlint"],
|
||||
"source": ["vscode_extensions"]
|
||||
},
|
||||
"filter": {
|
||||
"product": ["swiftlint"],
|
||||
"vendor": ["swiftlint_project"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"software": {
|
||||
"name": ["vknabel.vscode-swiftformat"],
|
||||
"source": ["vscode_extensions"]
|
||||
},
|
||||
"filter": {
|
||||
"product": ["swiftformat"],
|
||||
"vendor": ["swiftformat_project"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"software": {
|
||||
"name": ["jbenden.c-cpp-flylint"],
|
||||
"source": ["vscode_extensions"]
|
||||
},
|
||||
"filter": {
|
||||
"product": ["c\\/c\\+\\+_advanced_lint"],
|
||||
"vendor": ["c\\/c\\+\\+_advanced_lint_project"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"software": {
|
||||
"name": ["stripe.vscode-stripe"],
|
||||
"source": ["vscode_extensions"]
|
||||
},
|
||||
"filter": {
|
||||
"product": ["stripe"],
|
||||
"vendor": ["stripe"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"software": {
|
||||
"name": ["vscodevim.vim"],
|
||||
"source": ["vscode_extensions"]
|
||||
},
|
||||
"filter": {
|
||||
"product": ["vim"],
|
||||
"vendor": ["vim_project"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"software": {
|
||||
"name": ["svelte.svelte-vscode"],
|
||||
"source": ["vscode_extensions"]
|
||||
},
|
||||
"filter": {
|
||||
"product": ["svelte"],
|
||||
"vendor": ["svelte"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"software": {
|
||||
"name": ["ms-vscode-remote.remote-containers"],
|
||||
"source": ["vscode_extensions"]
|
||||
},
|
||||
"filter": {
|
||||
"product": ["remote"],
|
||||
"vendor": ["microsoft"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"software": {
|
||||
"name": ["ms-kubernetes-tools.vscode-kubernetes-tools"],
|
||||
"source": ["vscode_extensions"]
|
||||
},
|
||||
"filter": {
|
||||
"product": ["kubernetes_tools"],
|
||||
"vendor": ["microsoft"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"software": {
|
||||
"name": ["ms-dotnettools.vscode-dotnet-sdk"],
|
||||
"source": ["vscode_extensions"]
|
||||
},
|
||||
"filter": {
|
||||
"product": [".net_education_bundle_sdk_install_tool"],
|
||||
"vendor": ["microsoft"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"software": {
|
||||
"name": ["ms-dotnettools.vscode-dotnet-runtime"],
|
||||
"source": ["vscode_extensions"]
|
||||
},
|
||||
"filter": {
|
||||
"product": [".net_install_tool_for_extension_authors"],
|
||||
"vendor": ["microsoft"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"software": {
|
||||
"name": ["ms-vscode-remote.remote-wsl"],
|
||||
"source": ["vscode_extensions"]
|
||||
},
|
||||
"filter": {
|
||||
"product": ["windows_subsystem_for_linux"],
|
||||
"vendor": ["microsoft"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"software": {
|
||||
"name": ["mongodb.mongodb-vscode"],
|
||||
"source": ["vscode_extensions"]
|
||||
},
|
||||
"filter": {
|
||||
"product": ["mongodb"],
|
||||
"vendor": ["mongodb"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"software": {
|
||||
"name": ["oracle.mysql-shell-for-vs-code"],
|
||||
"source": ["vscode_extensions"]
|
||||
},
|
||||
"filter": {
|
||||
"product": ["mysql_shell"],
|
||||
"vendor": ["oracle"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"software": {
|
||||
"name": ["snyk-security.snyk-vulnerability-scanner"],
|
||||
"source": ["vscode_extensions"]
|
||||
},
|
||||
"filter": {
|
||||
"product": ["snyk_security"],
|
||||
"vendor": ["snyk"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"software": {
|
||||
"name": ["sourcegraph.cody-ai"],
|
||||
"source": ["vscode_extensions"]
|
||||
},
|
||||
"filter": {
|
||||
"product": ["cody"],
|
||||
"vendor": ["sourcegraph"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"software": {
|
||||
"name": ["github.vscode-pull-request-github"],
|
||||
"source": ["vscode_extensions"]
|
||||
},
|
||||
"filter": {
|
||||
"product": ["pull_requests_and_issues"],
|
||||
"vendor": ["github"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"software": {
|
||||
"name": ["Microsoft Edge"],
|
||||
|
||||
@@ -404,66 +404,69 @@ func checkCVEs(
|
||||
// No such vendor in the Vulnerability dictionary
|
||||
continue
|
||||
}
|
||||
cacheHits := cache.Get([]*wfn.Attributes{CPEItem.GetMeta()})
|
||||
for _, matches := range cacheHits {
|
||||
if len(matches.CPEs) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if rule, ok := knownNVDBugRules.FindMatch(
|
||||
matches.CVE.ID(),
|
||||
); ok {
|
||||
if !rule.CPEMatches(CPEItem.GetMeta()) {
|
||||
cpeItemsWithAliases := expandCPEAliases(CPEItem.GetMeta())
|
||||
for _, cpeItem := range cpeItemsWithAliases {
|
||||
cacheHits := cache.Get([]*wfn.Attributes{cpeItem})
|
||||
for _, matches := range cacheHits {
|
||||
if len(matches.CPEs) == 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// For chrome/firefox extensions we only want to match vulnerabilities
|
||||
// that are reported explicitly for target_sw == "chrome" or target_sw = "firefox".
|
||||
//
|
||||
// Why? In many ocassions the NVD dataset reports vulnerabilities in client applications
|
||||
// with target_sw == "*", meaning the client application is vulnerable on all operating systems.
|
||||
// Such rules we want to ignore here to prevent many false positives that do not apply to the
|
||||
// Chrome or Firefox environment.
|
||||
if CPEItem.GetMeta().TargetSW == "chrome" || CPEItem.GetMeta().TargetSW == "firefox" {
|
||||
if !matchesExactTargetSW(
|
||||
CPEItem.GetMeta().TargetSW,
|
||||
[]string{"chrome", "firefox"},
|
||||
matches.CVE.Config(),
|
||||
) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
resolvedVersion, err := getMatchingVersionEndExcluding(ctx, matches.CVE.ID(), CPEItem.GetMeta(), dict, logger)
|
||||
if err != nil {
|
||||
level.Debug(logger).Log("err", err)
|
||||
}
|
||||
|
||||
if _, ok := CPEItem.(softwareCPEWithNVDMeta); ok {
|
||||
|
||||
vuln := fleet.SoftwareVulnerability{
|
||||
SoftwareID: CPEItem.GetID(),
|
||||
CVE: matches.CVE.ID(),
|
||||
ResolvedInVersion: ptr.String(resolvedVersion),
|
||||
if rule, ok := knownNVDBugRules.FindMatch(
|
||||
matches.CVE.ID(),
|
||||
); ok {
|
||||
if !rule.CPEMatches(cpeItem) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
softwareMu.Lock()
|
||||
foundSoftwareVulns = append(foundSoftwareVulns, vuln)
|
||||
softwareMu.Unlock()
|
||||
} else if _, ok := CPEItem.(osCPEWithNVDMeta); ok {
|
||||
|
||||
vuln := fleet.OSVulnerability{
|
||||
OSID: CPEItem.GetID(),
|
||||
CVE: matches.CVE.ID(),
|
||||
ResolvedInVersion: ptr.String(resolvedVersion),
|
||||
// For chrome/firefox extensions we only want to match vulnerabilities
|
||||
// that are reported explicitly for target_sw == "chrome" or target_sw = "firefox".
|
||||
//
|
||||
// Why? In many occasions the NVD dataset reports vulnerabilities in client applications
|
||||
// with target_sw == "*", meaning the client application is vulnerable on all operating systems.
|
||||
// Such rules we want to ignore here to prevent many false positives that do not apply to the
|
||||
// Chrome or Firefox environment.
|
||||
if cpeItem.TargetSW == "chrome" || cpeItem.TargetSW == "firefox" {
|
||||
if !matchesExactTargetSW(
|
||||
cpeItem.TargetSW,
|
||||
[]string{"chrome", "firefox"},
|
||||
matches.CVE.Config(),
|
||||
) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
osMu.Lock()
|
||||
foundOSVulns = append(foundOSVulns, vuln)
|
||||
osMu.Unlock()
|
||||
}
|
||||
resolvedVersion, err := getMatchingVersionEndExcluding(ctx, matches.CVE.ID(), cpeItem, dict, logger)
|
||||
if err != nil {
|
||||
level.Debug(logger).Log("err", err)
|
||||
}
|
||||
|
||||
if _, ok := CPEItem.(softwareCPEWithNVDMeta); ok {
|
||||
vuln := fleet.SoftwareVulnerability{
|
||||
SoftwareID: CPEItem.GetID(),
|
||||
CVE: matches.CVE.ID(),
|
||||
ResolvedInVersion: ptr.String(resolvedVersion),
|
||||
}
|
||||
|
||||
softwareMu.Lock()
|
||||
foundSoftwareVulns = append(foundSoftwareVulns, vuln)
|
||||
softwareMu.Unlock()
|
||||
} else if _, ok := CPEItem.(osCPEWithNVDMeta); ok {
|
||||
|
||||
vuln := fleet.OSVulnerability{
|
||||
OSID: CPEItem.GetID(),
|
||||
CVE: matches.CVE.ID(),
|
||||
ResolvedInVersion: ptr.String(resolvedVersion),
|
||||
}
|
||||
|
||||
osMu.Lock()
|
||||
foundOSVulns = append(foundOSVulns, vuln)
|
||||
osMu.Unlock()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
case <-ctx.Done():
|
||||
level.Debug(logger).Log("msg", "quitting")
|
||||
@@ -485,6 +488,45 @@ func checkCVEs(
|
||||
return foundSoftwareVulns, foundOSVulns, nil
|
||||
}
|
||||
|
||||
// expandCPEAliases will generate new *wfn.Attributes from the given cpeItem.
|
||||
// It returns a slice with the given cpeItem plus the generated *wfn.Attributes.
|
||||
//
|
||||
// We need this because entries in the CPE database are not consistent.
|
||||
// E.g. some Visual Studio Code extensions are defined with target_sw=visual_studio_code
|
||||
// and others are defined with target_sw=visual_studio.
|
||||
// E.g. The python extension for Visual Studio Code is defined with
|
||||
// product=python_extension,target_sw=visual_studio_code and with
|
||||
// product=visual_studio_code,target_sw=python.
|
||||
func expandCPEAliases(cpeItem *wfn.Attributes) []*wfn.Attributes {
|
||||
cpeItems := []*wfn.Attributes{cpeItem}
|
||||
|
||||
// Some VSCode extensions are defined with target_sw=visual_studio_code
|
||||
// and others are defined with target_sw=visual_studio.
|
||||
for _, cpeItem := range cpeItems {
|
||||
if cpeItem.TargetSW == "visual_studio_code" {
|
||||
cpeItem2 := *cpeItem
|
||||
cpeItem2.TargetSW = "visual_studio"
|
||||
cpeItems = append(cpeItems, &cpeItem2)
|
||||
}
|
||||
}
|
||||
|
||||
// The python extension is defined in two ways in the CPE database:
|
||||
// cpe:2.3:a:microsoft:python_extension:2024.2.1:*:*:*:*:visual_studio_code:*:*
|
||||
// cpe:2.3:a:microsoft:visual_studio_code:2024.2.1:*:*:*:*:python:*:*
|
||||
for _, cpeItem := range cpeItems {
|
||||
if cpeItem.TargetSW == "visual_studio_code" &&
|
||||
cpeItem.Vendor == "microsoft" &&
|
||||
cpeItem.Product == "python_extension" {
|
||||
cpeItem2 := *cpeItem
|
||||
cpeItem2.Product = "visual_studio_code"
|
||||
cpeItem2.TargetSW = "python"
|
||||
cpeItems = append(cpeItems, &cpeItem2)
|
||||
}
|
||||
}
|
||||
|
||||
return cpeItems
|
||||
}
|
||||
|
||||
// Returns the versionEndExcluding string for the given CVE and host software meta
|
||||
// data, if it exists in the NVD feed. This effectively gives us the version of the
|
||||
// software it needs to upgrade to in order to address the CVE.
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
@@ -130,20 +131,26 @@ func (d *threadSafeDSMock) InsertSoftwareVulnerability(ctx context.Context, vuln
|
||||
}
|
||||
|
||||
func TestTranslateCPEToCVE(t *testing.T) {
|
||||
nettest.Run(t)
|
||||
|
||||
tempDir := t.TempDir()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// download the CVEs once for all sub-tests, and then disable syncing
|
||||
err := nettest.RunWithNetRetry(t, func() error {
|
||||
// We use cveFeedPrefixURL="https://nvd.nist.gov/feeds/json/cve/1.1/" because a full sync
|
||||
// with the NVD API 2.0 takes a long time (>15m). These feeds will be deprecated
|
||||
// on December 15th and this test will start failing then.
|
||||
return DownloadNVDCVEFeed(tempDir, "https://nvd.nist.gov/feeds/json/cve/1.1/", false, log.NewNopLogger())
|
||||
})
|
||||
require.NoError(t, err)
|
||||
// NVD_TEST_VULNDB_DIR can be used to speed up development (sync vulnerability data only once).
|
||||
tempDir := os.Getenv("NVD_TEST_VULNDB_DIR")
|
||||
if tempDir == "" {
|
||||
nettest.Run(t)
|
||||
// download the CVEs once for all sub-tests, and then disable syncing
|
||||
tempDir = t.TempDir()
|
||||
err := nettest.RunWithNetRetry(t, func() error {
|
||||
// We use cveFeedPrefixURL="https://nvd.nist.gov/feeds/json/cve/1.1/" because a full sync
|
||||
// with the NVD API 2.0 takes a long time (>15m). These feeds will be deprecated
|
||||
// TBD during 2024 and this test will start failing then.
|
||||
// For more information see: https://nvd.nist.gov/general/news/change-timeline.
|
||||
return DownloadNVDCVEFeed(tempDir, "https://nvd.nist.gov/feeds/json/cve/1.1/", false, log.NewNopLogger())
|
||||
})
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
require.DirExists(t, tempDir)
|
||||
t.Logf("Using %s as database path", tempDir)
|
||||
}
|
||||
|
||||
cveTests := map[string]struct {
|
||||
cpe string
|
||||
@@ -261,6 +268,48 @@ func TestTranslateCPEToCVE(t *testing.T) {
|
||||
},
|
||||
continuesToUpdate: true,
|
||||
},
|
||||
"cpe:2.3:a:microsoft:windows_subsystem_for_linux:0.63.10:*:*:*:*:visual_studio_code:*:*": {
|
||||
includedCVEs: []cve{
|
||||
{ID: "CVE-2021-43907", resolvedInVersion: "0.63.11"},
|
||||
},
|
||||
continuesToUpdate: false,
|
||||
},
|
||||
"cpe:2.3:a:github:pull_requests_and_issues:0.66.1:*:*:*:*:visual_studio_code:*:*": {
|
||||
includedCVEs: []cve{
|
||||
{ID: "CVE-2023-36867", resolvedInVersion: "0.66.2"},
|
||||
},
|
||||
continuesToUpdate: false,
|
||||
},
|
||||
"cpe:2.3:a:microsoft:python_extension:2020.9.1:*:*:*:*:visual_studio_code:*:*": {
|
||||
includedCVEs: []cve{
|
||||
{ID: "CVE-2020-17163", resolvedInVersion: "2020.9.2"},
|
||||
},
|
||||
continuesToUpdate: false,
|
||||
},
|
||||
"cpe:2.3:a:microsoft:jupyter:2023.10.10:*:*:*:*:visual_studio_code:*:*": {
|
||||
includedCVEs: []cve{
|
||||
{ID: "CVE-2023-36018", resolvedInVersion: "2023.10.1100000000"},
|
||||
},
|
||||
continuesToUpdate: false,
|
||||
},
|
||||
"cpe:2.3:a:microsoft:jupyter:2024.2.0:*:*:*:*:visual_studio_code:*:*": {
|
||||
includedCVEs: []cve{},
|
||||
continuesToUpdate: false,
|
||||
},
|
||||
"cpe:2.3:a:microsoft:visual_studio_code_eslint_extension:2.0.0:*:*:*:*:visual_studio_code:*:*": {
|
||||
includedCVEs: []cve{
|
||||
{ID: "CVE-2020-1481", resolvedInVersion: "2.1.7"},
|
||||
},
|
||||
continuesToUpdate: false,
|
||||
},
|
||||
"cpe:2.3:a:microsoft:python_extension:2020.4.0:*:*:*:*:visual_studio_code:*:*": {
|
||||
includedCVEs: []cve{
|
||||
{ID: "CVE-2020-1171", resolvedInVersion: "2020.5.0"},
|
||||
{ID: "CVE-2020-1192", resolvedInVersion: "2020.5.0"},
|
||||
{ID: "CVE-2020-17163", resolvedInVersion: "2020.9.2"},
|
||||
},
|
||||
continuesToUpdate: false,
|
||||
},
|
||||
}
|
||||
|
||||
cveOSTests := []struct {
|
||||
@@ -701,3 +750,70 @@ func loadDict(t *testing.T, path string) cvefeed.Dictionary {
|
||||
}
|
||||
return dict
|
||||
}
|
||||
|
||||
func TestExpandCPEAliases(t *testing.T) {
|
||||
firefox := &wfn.Attributes{
|
||||
Vendor: "mozilla",
|
||||
Product: "firefox",
|
||||
Version: "93.0.100",
|
||||
}
|
||||
chromePlugin := &wfn.Attributes{
|
||||
Vendor: "google",
|
||||
Product: "plugin foobar",
|
||||
Version: "93.0.100",
|
||||
TargetSW: "chrome",
|
||||
}
|
||||
|
||||
vsCodeExtension := &wfn.Attributes{
|
||||
Vendor: "Microsoft",
|
||||
Product: "foo.extension",
|
||||
Version: "2024.2.1",
|
||||
TargetSW: "visual_studio_code",
|
||||
}
|
||||
vsCodeExtensionAlias := *vsCodeExtension
|
||||
vsCodeExtensionAlias.TargetSW = "visual_studio"
|
||||
|
||||
pythonCodeExtension := &wfn.Attributes{
|
||||
Vendor: "microsoft",
|
||||
Product: "python_extension",
|
||||
Version: "2024.2.1",
|
||||
TargetSW: "visual_studio_code",
|
||||
}
|
||||
pythonCodeExtensionAlias1 := *pythonCodeExtension
|
||||
pythonCodeExtensionAlias1.TargetSW = "visual_studio"
|
||||
pythonCodeExtensionAlias2 := *pythonCodeExtension
|
||||
pythonCodeExtensionAlias2.Product = "visual_studio_code"
|
||||
pythonCodeExtensionAlias2.TargetSW = "python"
|
||||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
cpeItem *wfn.Attributes
|
||||
expectedAliases []*wfn.Attributes
|
||||
}{
|
||||
{
|
||||
name: "no expansion without target_sw",
|
||||
cpeItem: firefox,
|
||||
expectedAliases: []*wfn.Attributes{firefox},
|
||||
},
|
||||
{
|
||||
name: "no expansion with target_sw",
|
||||
cpeItem: chromePlugin,
|
||||
expectedAliases: []*wfn.Attributes{chromePlugin},
|
||||
},
|
||||
{
|
||||
name: "visual studio code extension",
|
||||
cpeItem: vsCodeExtension,
|
||||
expectedAliases: []*wfn.Attributes{vsCodeExtension, &vsCodeExtensionAlias},
|
||||
},
|
||||
{
|
||||
name: "python visual studio code extension",
|
||||
cpeItem: pythonCodeExtension,
|
||||
expectedAliases: []*wfn.Attributes{pythonCodeExtension, &pythonCodeExtensionAlias1, &pythonCodeExtensionAlias2},
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
aliases := expandCPEAliases(tc.cpeItem)
|
||||
require.Equal(t, tc.expectedAliases, aliases)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,6 +145,14 @@ func productVariations(s *fleet.Software) []string {
|
||||
r = append(r, re)
|
||||
}
|
||||
|
||||
// VSCode extensions have a unique s.Name of the form "<vendor>.<extension>" (aka extension ID)
|
||||
if s.Source == "vscode_extensions" {
|
||||
parts := strings.SplitN(s.Name, ".", 2)
|
||||
if len(parts) == 2 && parts[1] != "" {
|
||||
r = append(r, parts[1])
|
||||
}
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
@@ -178,6 +186,14 @@ func vendorVariations(s *fleet.Software) []string {
|
||||
}
|
||||
}
|
||||
|
||||
// VSCode extensions have a unique s.Name of the form "<vendor>.<extension>" (aka extension ID)
|
||||
if s.Source == "vscode_extensions" {
|
||||
parts := strings.SplitN(s.Name, ".", 2)
|
||||
if len(parts) == 2 && parts[0] != "" {
|
||||
r = append(r, parts[0])
|
||||
}
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
@@ -211,6 +227,8 @@ func targetSW(s *fleet.Software) string {
|
||||
return `node.js`
|
||||
case "programs":
|
||||
return "windows"
|
||||
case "vscode_extensions":
|
||||
return "visual_studio_code"
|
||||
}
|
||||
return "*"
|
||||
}
|
||||
|
||||
@@ -157,6 +157,11 @@ func TestVariations(t *testing.T) {
|
||||
vendorVariations: []string{"apple", "python3"},
|
||||
productVariations: []string{"python"},
|
||||
},
|
||||
{
|
||||
software: fleet.Software{Name: "ms-python.python", Version: "3.8.9", BundleIdentifier: "", Source: "vscode_extensions", Vendor: "Microsoft"},
|
||||
vendorVariations: []string{"microsoft", "ms-python"},
|
||||
productVariations: []string{"python", "ms-python.python"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range variationsTestCases {
|
||||
|
||||
Reference in New Issue
Block a user