fleet/server/vulnerabilities/nvd/cve_test.go
Konstantin Sykulev 28704fe447
Fixed Safari cve false positives (#38903)
**Related issue:** Resolves #35194

The NVD database for CVE-2023-28205 contains two broad CPE match rules:
Safari: Any version < 16.4.1 is vulnerable
macOS: Any version < 13.3.1 is vulnerable

The problem is Safari versions 13.x, 14.x, and 15.x were never actually
vulnerable to this CVE and macOS versions 10.x, 11.x, and 12.x never
received a system-level fix for this CVE.

Apple fixed the cve in two ways
1. Safari 16.4.1 standalone update
2. macOS Ventura 13.3.1 system update fix at the OS level

This is why there is such a complicated `IgnoreIf` for the
`CPEMatchingRule`.

If some of the following don't apply, delete the relevant line.
- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.

## Testing

- [x] Added/updated automated tests
- [ ] QA'd all new/changed functionality manually

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

## Release Notes

* **Bug Fixes**
* Improved accuracy of Safari CVE-2023-28205 vulnerability detection
with version-specific filtering for Safari 16.0-16.4.0 and macOS
Ventura.

* **Tests**
* Added comprehensive test coverage for CVE-2023-28205 across multiple
Safari versions and macOS releases.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-01-29 15:51:01 -06:00

1326 lines
40 KiB
Go

package nvd
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"os"
"strings"
"sync"
"testing"
"time"
"github.com/fleetdm/fleet/v4/pkg/nettest"
"github.com/fleetdm/fleet/v4/server/fleet"
"github.com/fleetdm/fleet/v4/server/mock"
"github.com/fleetdm/fleet/v4/server/vulnerabilities/nvd/tools/cvefeed"
"github.com/fleetdm/fleet/v4/server/vulnerabilities/nvd/tools/wfn"
"github.com/go-kit/log"
kitlog "github.com/go-kit/log"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// firefox93WindowsVulnerabilities was manually generated by visiting:
// https://nvd.nist.gov/vuln/search/results?form_type=Advanced&results_type=overview&isCpeNameSearch=true&seach_type=all&query=cpe:2.3:a:mozilla:firefox:93.0:*:*:*:*:*:*:*
type cve struct {
ID string
resolvedInVersion string
}
var firefox93WindowsVulnerabilities = []cve{
{ID: "CVE-2021-43540", resolvedInVersion: "95.0"},
{ID: "CVE-2021-38503", resolvedInVersion: "94.0"},
{ID: "CVE-2021-38504", resolvedInVersion: "94.0"},
{ID: "CVE-2021-38506", resolvedInVersion: "94.0"},
{ID: "CVE-2021-38507", resolvedInVersion: "94.0"},
{ID: "CVE-2021-38508", resolvedInVersion: "94.0"},
{ID: "CVE-2021-38509", resolvedInVersion: "94.0"},
{ID: "CVE-2021-43534", resolvedInVersion: "94.0"},
{ID: "CVE-2021-43532", resolvedInVersion: "94.0"},
{ID: "CVE-2021-43531", resolvedInVersion: "94.0"},
{ID: "CVE-2021-43533", resolvedInVersion: "94.0"},
{ID: "CVE-2021-43538", resolvedInVersion: "95.0"},
{ID: "CVE-2021-43542", resolvedInVersion: "95.0"},
{ID: "CVE-2021-43543", resolvedInVersion: "95.0"},
{ID: "CVE-2021-30547"},
{ID: "CVE-2021-43546", resolvedInVersion: "95.0"},
{ID: "CVE-2021-43537", resolvedInVersion: "95.0"},
{ID: "CVE-2021-43541", resolvedInVersion: "95.0"},
{ID: "CVE-2021-43536", resolvedInVersion: "95.0"},
{ID: "CVE-2021-43545", resolvedInVersion: "95.0"},
{ID: "CVE-2021-43539", resolvedInVersion: "95.0"},
{ID: "CVE-2022-34480", resolvedInVersion: "102.0"},
{ID: "CVE-2022-26387", resolvedInVersion: "98.0"},
{ID: "CVE-2022-22759", resolvedInVersion: "97.0"},
{ID: "CVE-2022-28281", resolvedInVersion: "99.0"},
{ID: "CVE-2022-45415", resolvedInVersion: "107.0"},
{ID: "CVE-2022-42930", resolvedInVersion: "106.0"},
{ID: "CVE-2022-0511", resolvedInVersion: "97.0"},
{ID: "CVE-2022-22763", resolvedInVersion: "96.0"},
{ID: "CVE-2022-22737", resolvedInVersion: "96.0"},
{ID: "CVE-2022-22751", resolvedInVersion: "96.0"},
{ID: "CVE-2022-38478", resolvedInVersion: "104.0"},
{ID: "CVE-2022-22761", resolvedInVersion: "97.0"},
{ID: "CVE-2022-34482", resolvedInVersion: "102.0"},
{ID: "CVE-2022-26486", resolvedInVersion: "97.0.2"},
{ID: "CVE-2022-22739", resolvedInVersion: "96.0"},
{ID: "CVE-2022-22755", resolvedInVersion: "97.0"},
{ID: "CVE-2022-22757", resolvedInVersion: "97.0"},
{ID: "CVE-2022-1097", resolvedInVersion: "99.0"},
{ID: "CVE-2022-22754", resolvedInVersion: "97.0"},
{ID: "CVE-2022-22748", resolvedInVersion: "96.0"},
{ID: "CVE-2022-22736", resolvedInVersion: "96.0"},
{ID: "CVE-2022-22745", resolvedInVersion: "96.0"},
{ID: "CVE-2022-26385", resolvedInVersion: "98.0"},
{ID: "CVE-2022-26383", resolvedInVersion: "98.0"},
{ID: "CVE-2022-3266", resolvedInVersion: "105.0"},
{ID: "CVE-2022-34468", resolvedInVersion: "102.0"},
{ID: "CVE-2022-34481", resolvedInVersion: "102.0"},
{ID: "CVE-2022-28289", resolvedInVersion: "99.0"},
{ID: "CVE-2022-22741", resolvedInVersion: "96.0"},
{ID: "CVE-2022-28284", resolvedInVersion: "99.0"},
{ID: "CVE-2022-34484", resolvedInVersion: "102.0"},
{ID: "CVE-2022-22752", resolvedInVersion: "96.0"},
{ID: "CVE-2022-26485", resolvedInVersion: "97.0.2"},
{ID: "CVE-2022-28286", resolvedInVersion: "99.0"},
{ID: "CVE-2022-28283", resolvedInVersion: "99.0"},
{ID: "CVE-2022-28285", resolvedInVersion: "99.0"},
{ID: "CVE-2022-0843", resolvedInVersion: "97.0"},
{ID: "CVE-2022-29909", resolvedInVersion: "100.0"},
{ID: "CVE-2022-22749", resolvedInVersion: "96.0"},
{ID: "CVE-2022-26384", resolvedInVersion: "98.0"},
{ID: "CVE-2022-28282", resolvedInVersion: "99.0"},
{ID: "CVE-2022-28287", resolvedInVersion: "99.0"},
{ID: "CVE-2022-40956", resolvedInVersion: "105.0"},
{ID: "CVE-2022-22740", resolvedInVersion: "96.0"},
{ID: "CVE-2022-22743", resolvedInVersion: "96.0"},
{ID: "CVE-2022-22764", resolvedInVersion: "97.0"},
{ID: "CVE-2022-22738", resolvedInVersion: "96.0"},
{ID: "CVE-2022-1529", resolvedInVersion: "100.0.2"},
{ID: "CVE-2022-22760", resolvedInVersion: "97.0"},
{ID: "CVE-2022-29916", resolvedInVersion: "100.0"},
{ID: "CVE-2022-29917", resolvedInVersion: "100.0"},
{ID: "CVE-2022-22747", resolvedInVersion: "96.0"},
{ID: "CVE-2022-26382", resolvedInVersion: "98.0"},
{ID: "CVE-2022-22742", resolvedInVersion: "96.0"},
{ID: "CVE-2022-28288", resolvedInVersion: "99.0"},
{ID: "CVE-2022-22756", resolvedInVersion: "97.0"},
{ID: "CVE-2022-26381", resolvedInVersion: "98.0"},
{ID: "CVE-2022-1802", resolvedInVersion: "100.0.2"},
{ID: "CVE-2022-34483", resolvedInVersion: "102.0"},
{ID: "CVE-2022-29915", resolvedInVersion: "100.0"},
}
type threadSafeDSMock struct {
mu sync.Mutex
*mock.Store
}
func (d *threadSafeDSMock) ListSoftwareCPEs(ctx context.Context) ([]fleet.SoftwareCPE, error) {
d.mu.Lock()
defer d.mu.Unlock()
return d.Store.ListSoftwareCPEs(ctx)
}
func (d *threadSafeDSMock) InsertSoftwareVulnerability(ctx context.Context, vuln fleet.SoftwareVulnerability, src fleet.VulnerabilitySource) (bool, error) {
d.mu.Lock()
defer d.mu.Unlock()
return d.Store.InsertSoftwareVulnerability(ctx, vuln, src)
}
func TestTranslateCPEToCVE(t *testing.T) {
t.Parallel()
ctx := context.Background()
// 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 == "" {
// download the CVEs once for all sub-tests, and then disable syncing
tempDir = t.TempDir()
err := nettest.RunWithNetRetry(t, func() error {
return DownloadCVEFeed(tempDir, "", 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
excludedCVEs []string
includedCVEs []cve
// continuesToUpdate indicates if the product/software
// continues to register new CVE vulnerabilities.
continuesToUpdate bool
}{
"cpe:2.3:a:1password:1password:3.9.9:*:*:*:*:macos:*:*": {
includedCVEs: []cve{
{ID: "CVE-2012-6369"},
},
continuesToUpdate: false,
},
"cpe:2.3:a:1password:1password:3.9.9:*:*:*:*:*:*:*": {
includedCVEs: []cve{
{ID: "CVE-2012-6369"},
},
continuesToUpdate: false,
},
"cpe:2.3:a:pypa:pip:9.0.3:*:*:*:*:python:*:*": {
includedCVEs: []cve{
{ID: "CVE-2019-20916", resolvedInVersion: "19.2"},
{ID: "CVE-2021-3572", resolvedInVersion: "21.1"},
{ID: "CVE-2025-8869", resolvedInVersion: "25.3"},
{ID: "CVE-2023-5752", resolvedInVersion: "23.3"},
},
continuesToUpdate: false,
},
"cpe:2.3:a:mozilla:firefox:93.0:*:*:*:*:windows:*:*": {
includedCVEs: firefox93WindowsVulnerabilities,
continuesToUpdate: true,
},
"cpe:2.3:a:mozilla:firefox:93.0.100:*:*:*:*:windows:*:*": {
includedCVEs: firefox93WindowsVulnerabilities,
continuesToUpdate: true,
},
"cpe:2.3:a:apple:icloud:1.0:*:*:*:*:macos:*:*": {
excludedCVEs: []string{
"CVE-2017-13797",
"CVE-2017-2383",
"CVE-2017-2366",
"CVE-2016-4613",
"CVE-2016-4692",
"CVE-2016-4743",
"CVE-2016-7578",
"CVE-2016-7583",
"CVE-2016-7586",
"CVE-2016-7587",
"CVE-2016-7589",
"CVE-2016-7592",
"CVE-2016-7598",
"CVE-2016-7599",
"CVE-2016-7610",
"CVE-2016-7611",
"CVE-2016-7614",
"CVE-2016-7632",
"CVE-2016-7635",
"CVE-2016-7639",
"CVE-2016-7640",
"CVE-2016-7641",
"CVE-2016-7642",
"CVE-2016-7645",
"CVE-2016-7646",
"CVE-2016-7648",
"CVE-2016-7649",
"CVE-2016-7652",
"CVE-2016-7654",
"CVE-2016-7656",
"CVE-2017-2383",
},
continuesToUpdate: true,
},
"cpe:2.3:a:clickstudios:passwordstate:9.5.8.4:*:*:*:*:chrome:*:*": {
includedCVEs: []cve{
{ID: "CVE-2022-4610", resolvedInVersion: ""},
{ID: "CVE-2022-4611", resolvedInVersion: ""},
{ID: "CVE-2022-4613", resolvedInVersion: ""},
{ID: "CVE-2022-4612", resolvedInVersion: ""},
},
continuesToUpdate: true,
},
"cpe:2.3:a:apple:garageband:10.4.11:*:*:*:*:macos:*:*": {
excludedCVEs: []string{"CVE-2024-54559"},
continuesToUpdate: true,
},
"cpe:2.3:o:apple:macos:15.1.1:*:*:*:*:*:*:*": {
includedCVEs: []cve{{ID: "CVE-2024-54559", resolvedInVersion: "15.2"}},
continuesToUpdate: true,
},
"cpe:2.3:a:avira:password_manager:2.18.4.38471:*:*:*:*:firefox:*:*": {
includedCVEs: []cve{
{ID: "CVE-2022-28795"},
},
continuesToUpdate: true,
},
"cpe:2.3:a:zoom:zoom:5.0.4301.0407:*:*:*:*:chrome:*:*": {
excludedCVEs: []string{"CVE-2021-28133"}, // CVE-2021-28133 is a vulnerability in the Zoom application, not the extension.
continuesToUpdate: true,
},
"cpe:2.3:a:bitwarden:bitwarden:1.55.0:*:*:*:*:firefox:*:*": {
excludedCVEs: []string{"CVE-2023-38840"}, // CVE-2023-38840 is a vulnerability in the Bitwarden application, not the extension.
continuesToUpdate: true,
},
// No Version Start
"cpe:2.3:a:python:setuptools:64:*:*:*:*:*:*:*": {
includedCVEs: []cve{
{ID: "CVE-2022-40897", resolvedInVersion: "65.5.1"},
},
continuesToUpdate: true,
},
"cpe:2.3:a:mozilla:firefox:93.0.100:*:*:*:*:*:*:*": {
includedCVEs: []cve{
// CVE matches multiple products
{ID: "CVE-2022-40956", resolvedInVersion: "105.0"},
},
continuesToUpdate: true,
},
// Multiple product matches with different version ranges
"cpe:2.3:o:apple:macos:14.1:*:*:*:*:*:*:*": {
includedCVEs: []cve{
{ID: "CVE-2023-42919", resolvedInVersion: "14.2"},
},
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"},
{ID: "CVE-2024-49050", resolvedInVersion: "2024.18.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:eslint: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"},
{ID: "CVE-2024-49050", resolvedInVersion: "2024.18.2"},
},
continuesToUpdate: false,
},*/
// #34323
"cpe:2.3:a:valvesoftware:dota_2:1.0:*:*:*:*:macos:*:*": {
excludedCVEs: []string{
"CVE-2020-7949",
"CVE-2020-7950",
"CVE-2020-7951",
"CVE-2020-7952",
"CVE-2020-9005",
},
},
"cpe:2.3:a:valvesoftware:dota_2:1.0.0:*:*:*:*:macos:*:*": {
excludedCVEs: []string{
"CVE-2020-7949",
"CVE-2020-7950",
"CVE-2020-7951",
"CVE-2020-7952",
"CVE-2020-9005",
},
},
"cpe:2.3:a:valvesoftware:dota_2:1.337:*:*:*:*:macos:*:*": {
includedCVEs: []cve{
{ID: "CVE-2020-9005" /*resolvedInVersion: "2020-02-17"*/}, // we don't do non-semver resolved-in
{ID: "CVE-2020-7950", resolvedInVersion: "7.23f"},
},
continuesToUpdate: true,
},
// end of #34323
"cpe:2.3:a:adobe:animate:*:*:*:*:*:macos:*:*": {
includedCVEs: []cve{
{ID: "CVE-2023-44325"},
},
continuesToUpdate: true,
},
"cpe:2.3:a:apple:safari:17.0:*:*:*:*:macos:*:*": {
includedCVEs: []cve{
{ID: "CVE-2023-42852", resolvedInVersion: "17.1"},
{ID: "CVE-2023-42950", resolvedInVersion: "17.2"},
{ID: "CVE-2024-23273", resolvedInVersion: "17.4"},
},
excludedCVEs: []string{
"CVE-2023-28205", // This vulnerability is for Safari 16.4.0
"CVE-2024-23252", // Rejected CVE
},
continuesToUpdate: true,
},
"cpe:2.3:a:apple:safari:16.4.0:*:*:*:*:macos:*:*": {
includedCVEs: []cve{
{ID: "CVE-2023-28205", resolvedInVersion: "16.4.1"},
},
continuesToUpdate: true,
},
"cpe:2.3:a:apple:safari:16.2:*:*:*:*:macos:*:*": {
includedCVEs: []cve{
{ID: "CVE-2023-28205", resolvedInVersion: "16.4.1"},
},
continuesToUpdate: true,
},
"cpe:2.3:a:apple:safari:16.4.1:*:*:*:*:macos:*:*": {
excludedCVEs: []string{
"CVE-2023-28205",
},
continuesToUpdate: true,
},
"cpe:2.3:a:apple:safari:16.5:*:*:*:*:macos:*:*": {
excludedCVEs: []string{
"CVE-2023-28205",
},
continuesToUpdate: true,
},
"cpe:2.3:a:apple:safari:16.6:*:*:*:*:macos:*:*": {
excludedCVEs: []string{
"CVE-2023-28205",
},
continuesToUpdate: true,
},
"cpe:2.3:a:apple:safari:15.6.1:*:*:*:*:macos:*:*": {
excludedCVEs: []string{
"CVE-2023-28205",
},
continuesToUpdate: true,
},
"cpe:2.3:a:apple:safari:14.1.2:*:*:*:*:macos:*:*": {
excludedCVEs: []string{
"CVE-2023-28205",
},
continuesToUpdate: true,
},
"cpe:2.3:a:apple:safari:13.1.3:*:*:*:*:macos:*:*": {
excludedCVEs: []string{
"CVE-2023-28205",
},
continuesToUpdate: true,
},
"cpe:2.3:a:microsoft:365_apps:16.0.17628.20144:*:*:*:*:windows:*:*": {
includedCVEs: []cve{
{ID: "CVE-2024-21402"},
},
excludedCVEs: []string{"CVE-2011-5049"}, // OS vulnerability
continuesToUpdate: true,
},
"cpe:2.3:a:citrix:workspace:2309.0:*:*:*:*:macos:*:*": {
excludedCVEs: []string{"CVE-2024-6286"},
continuesToUpdate: true,
},
// FIXME: https://github.com/fleetdm/fleet/issues/31303
// "cpe:2.3:a:citrix:workspace:2309.0:*:*:*:*:windows:*:*": {
// includedCVEs: []cve{
// {ID: "CVE-2024-6286", resolvedInVersion: "2402"},
// },
// continuesToUpdate: true,
// },
"cpe:2.3:a:python:python:3.9.6:*:*:*:*:macos:*:*": {
excludedCVEs: []string{"CVE-2024-4030"},
continuesToUpdate: true,
},
// Skipping test while troubleshooting https://github.com/fleetdm/fleet/issues/24286
//
// "cpe:2.3:a:python:python:3.9.6:*:*:*:*:windows:*:*": {
// includedCVEs: []cve{
// {ID: "CVE-2024-4030", resolvedInVersion: "3.9.20"},
// },
// continuesToUpdate: true,
// },
// Tests the expandCPEAliases rule for virtualbox on macOS
"cpe:2.3:a:oracle:virtualbox:7.0.6:*:*:*:*:macos:*:*": {
includedCVEs: []cve{
{ID: "CVE-2023-21989", resolvedInVersion: "7.0.8"},
{ID: "CVE-2024-21141", resolvedInVersion: "7.0.20"},
},
continuesToUpdate: true,
},
"cpe:2.3:a:mozilla:firefox:*:*:*:*:*:*:*:*": {
excludedCVEs: []string{"CVE-2024-10004"},
continuesToUpdate: true,
},
"cpe:2.3:a:okta:verify:9.27.0:*:*:*:*:macos:*:*": {
excludedCVEs: []string{"CVE-2024-10327"},
continuesToUpdate: true,
},
// CVE-2023-48795 false positive and true positive checks (see #26073)
"cpe:2.3:a:microsoft:powershell:7.4.3:*:*:*:*:*:*:*": {
excludedCVEs: []string{"CVE-2023-48795", "CVE-2025-21171"},
continuesToUpdate: true,
},
"cpe:2.3:a:openbsd:openssh:9.5:p1:*:*:*:*:*:*": {
includedCVEs: []cve{{ID: "CVE-2023-48795", resolvedInVersion: "9.6"}},
continuesToUpdate: true,
},
"cpe:2.3:a:openbsd:openssh:9.6:*:*:*:*:*:*": {
excludedCVEs: []string{"CVE-2023-48795"},
continuesToUpdate: true,
},
// end of CVE-2023-48795 checks
// CVE-2025-21171 handling
// https://github.com/fleetdm/fleet/pull/30767
// NVD lists this CVE for version 7.5 and not 7.5.0
"cpe:2.3:a:microsoft:powershell:7.5.0:*:*:*:*:macos:*:*": {
excludedCVEs: []string{"CVE-2025-21171"},
continuesToUpdate: true,
},
"cpe:2.3:a:microsoft:powershell:7.5.0:*:*:*:*:windows:*:*": {
excludedCVEs: []string{"CVE-2025-21171"},
includedCVEs: []cve{{ID: "CVE-2025-30399"}},
continuesToUpdate: true,
},
"cpe:2.3:a:microsoft:powershell:7.5.2:*:*:*:*:windows:*:*": {
excludedCVEs: []string{"CVE-2025-21171", "CVE-2025-30399"},
continuesToUpdate: true,
},
"cpe:2.3:a:microsoft:powershell:7.5:*:*:*:*:macos:*:*": {
includedCVEs: []cve{{ID: "CVE-2025-21171"}},
continuesToUpdate: true,
},
"cpe:2.3:a:microsoft:powershell:7.5:*:*:*:*:windows:*:*": {
includedCVEs: []cve{{ID: "CVE-2025-21171"}},
continuesToUpdate: true,
},
// end of CVE-2025-21171 checks
"cpe:2.3:a:jetbrains:goland:2022.3.99.123.456:*:*:*:*:macos:*:*": {
includedCVEs: []cve{{ID: "CVE-2024-37051", resolvedInVersion: "2023.1.6"}},
continuesToUpdate: true,
},
"cpe:2.3:a:jetbrains:goland:2024.3:*:*:*:*:macos:*:*": {
excludedCVEs: []string{"CVE-2024-37051"},
continuesToUpdate: true,
},
"cpe:2.3:a:iterm2:iterm2:3.5.1:*:*:*:*:*:*:*": {
includedCVEs: []cve{
{ID: "CVE-2024-38395", resolvedInVersion: "3.5.2"},
// NVD is being flakey so may not show this // {ID: "CVE-2024-38396", resolvedInVersion: "3.5.2"},
},
continuesToUpdate: true,
},
"cpe:2.3:a:simple_password_store_project:simple_password_store:1.7.0:*:*:*:*:macos:*:*": {
includedCVEs: []cve{{ID: "CVE-2018-12356", resolvedInVersion: "1.7.2"}},
},
"cpe:2.3:a:python:python:3.12.0:*:*:*:*:windows:*:*": {
excludedCVEs: []string{"CVE-2024-12254"},
continuesToUpdate: true,
},
"cpe:2.3:a:python:python:3.13.0:*:*:*:*:windows:*:*": {
excludedCVEs: []string{"CVE-2024-12254"},
continuesToUpdate: true,
},
"cpe:2.3:a:python:python:3.14.0:alpha1:*:*:*:windows:*:*": {
excludedCVEs: []string{"CVE-2024-12254"},
continuesToUpdate: true,
},
"cpe:2.3:a:python:python:3.14.0:alpha2:*:*:*:windows:*:*": {
excludedCVEs: []string{"CVE-2024-12254"},
continuesToUpdate: true,
},
"cpe:2.3:a:python:python:3.12.0:-:*:*:*:macos:*:*": {
includedCVEs: []cve{
{
ID: "CVE-2025-1795",
resolvedInVersion: "3.12.3",
},
{
ID: "CVE-2024-7592",
resolvedInVersion: "3.12.6",
},
{
ID: "CVE-2024-6923",
resolvedInVersion: "3.12.5",
},
{
ID: "CVE-2024-0397",
resolvedInVersion: "3.12.3",
},
{
ID: "CVE-2024-12254",
resolvedInVersion: "3.12.9",
},
{
ID: "CVE-2024-9287",
resolvedInVersion: "3.12.8",
},
{
ID: "CVE-2025-0938",
resolvedInVersion: "3.12.9",
},
{
ID: "CVE-2023-6507",
// TODO: fix missing version here (according to vulncheck it was fixed in
// 3.12.1, but the generated feed data doesn't have this value)
resolvedInVersion: "",
},
{
ID: "CVE-2024-8088",
resolvedInVersion: "3.12.6",
},
{
ID: "CVE-2024-4032",
resolvedInVersion: "3.12.4",
},
{
ID: "CVE-2024-3219",
resolvedInVersion: "3.12.5",
},
{
ID: "CVE-2024-0450",
resolvedInVersion: "3.12.2",
},
{
ID: "CVE-2023-6597",
resolvedInVersion: "3.12.1",
},
{
ID: "CVE-2024-3220",
resolvedInVersion: "3.14.0",
},
{
ID: "CVE-2024-6232",
resolvedInVersion: "3.12.6",
},
},
continuesToUpdate: true,
},
"cpe:2.3:a:python:python:3.14.0:alpha1:*:*:*:macos:*:*": {
includedCVEs: []cve{
{
ID: "CVE-2024-12254",
resolvedInVersion: "3.14.0a3",
},
{
ID: "CVE-2024-9287",
resolvedInVersion: "",
},
{
ID: "CVE-2025-0938",
resolvedInVersion: "3.14.0a5",
},
},
continuesToUpdate: true,
},
"cpe:2.3:a:python:python:3.14.0:alpha2:*:*:*:macos:*:*": {
includedCVEs: []cve{
{
ID: "CVE-2024-12254",
resolvedInVersion: "3.14.0a3",
},
{
ID: "CVE-2025-0938",
resolvedInVersion: "3.14.0a5",
},
},
continuesToUpdate: true,
},
"cpe:2.3:a:python:python:3.14.0:alpha3:*:*:*:macos:*:*": {
excludedCVEs: []string{"CVE-2024-12254"},
includedCVEs: []cve{
{
ID: "CVE-2025-0938",
resolvedInVersion: "3.14.0a5",
},
},
continuesToUpdate: true,
},
"cpe:2.3:a:google:chrome:138.0.7204.91:*:*:*:*:linux:*:*": {
excludedCVEs: []string{"CVE-2025-6554"},
continuesToUpdate: true,
},
"cpe:2.3:a:google:chrome:138.0.7204.92:*:*:*:*:macos:*:*": {
excludedCVEs: []string{"CVE-2025-6554"},
continuesToUpdate: true,
},
"cpe:2.3:a:docker:desktop:4.43.2:*:*:*:*:macos:*:*": {
includedCVEs: []cve{{ID: "CVE-2025-9074", resolvedInVersion: "4.44.3"}},
continuesToUpdate: true,
},
"cpe:2.3:a:docker:desktop:4.39.0:*:*:*:*:windows:*:*": {
includedCVEs: []cve{{ID: "CVE-2025-9074", resolvedInVersion: "4.44.3"}},
continuesToUpdate: true,
},
}
cveOSTests := []struct {
platform string
version string
osID uint
includedCVEs []string
excludedCVEs []string
}{
{
platform: "darwin",
version: "14.1.2",
osID: 1,
includedCVEs: []string{
"CVE-2023-45866",
"CVE-2023-42886",
"CVE-2023-42891",
"CVE-2023-42906",
"CVE-2023-42910",
"CVE-2023-42924",
"CVE-2023-42883",
"CVE-2023-42894",
"CVE-2023-42926",
"CVE-2023-42932",
"CVE-2023-42907",
"CVE-2023-42922",
"CVE-2023-42904",
"CVE-2023-42901",
"CVE-2023-42898",
"CVE-2023-42903",
"CVE-2023-42902",
"CVE-2023-42909",
"CVE-2023-42914",
"CVE-2023-42874",
"CVE-2023-42882",
"CVE-2023-42912",
"CVE-2023-42911",
"CVE-2023-42890",
"CVE-2023-42905",
"CVE-2023-42919",
"CVE-2023-42900",
"CVE-2023-42899",
"CVE-2023-42908",
"CVE-2023-42884",
},
},
{
platform: "darwin",
version: "13.6.2",
osID: 2,
// This is a subset of vulnerabilities for macOS 13.6.2
includedCVEs: []string{
"CVE-2023-32361",
"CVE-2023-35990",
"CVE-2023-40541",
"CVE-2023-40400",
"CVE-2023-41980",
"CVE-2023-38615",
"CVE-2023-39233",
"CVE-2023-40402",
"CVE-2023-40450",
"CVE-2023-42891",
"CVE-2023-41079",
"CVE-2023-42932",
"CVE-2023-38586",
"CVE-2023-41067",
"CVE-2023-40407",
"CVE-2023-42924",
"CVE-2023-40395",
"CVE-2023-38596",
"CVE-2023-32396",
"CVE-2023-29497",
},
},
{
platform: "darwin",
version: "15.3",
osID: 3,
// This was resolved in 15.3, so it should be excluded. See https://github.com/fleetdm/fleet/issues/26561.
excludedCVEs: []string{"CVE-2025-24176"},
},
{
platform: "darwin",
version: "13.2",
osID: 4,
// macOS Ventura 13.2 < 13.3.1 is vulnerable to CVE-2023-28205
includedCVEs: []string{"CVE-2023-28205"},
},
{
platform: "darwin",
version: "13.3",
osID: 5,
// macOS Ventura 13.3.0 is vulnerable (< 13.3.1)
includedCVEs: []string{"CVE-2023-28205"},
},
{
platform: "darwin",
version: "13.3.1",
osID: 6,
// macOS Ventura 13.3.1 includes system-level WebKit patch for CVE-2023-28205
excludedCVEs: []string{"CVE-2023-28205"},
},
{
platform: "darwin",
version: "13.4",
osID: 7,
// macOS Ventura 13.4 is patched (>= 13.3.1)
excludedCVEs: []string{"CVE-2023-28205"},
},
{
platform: "darwin",
version: "13.5",
osID: 8,
// macOS Ventura 13.5 is patched (>= 13.3.1)
excludedCVEs: []string{"CVE-2023-28205"},
},
{
platform: "darwin",
version: "12.7.6",
osID: 9,
// macOS Monterey 12.7.6 did NOT receive system-level fix (rely on Safari matching)
excludedCVEs: []string{"CVE-2023-28205"},
},
{
platform: "darwin",
version: "12.6.5",
osID: 10,
// macOS Monterey 12.6.5 did NOT fix CVE-2023-28205 (only fixed CVE-2023-28206)
excludedCVEs: []string{"CVE-2023-28205"},
},
{
platform: "darwin",
version: "11.7.10",
osID: 11,
// macOS Big Sur 11.7.10 did NOT receive system-level fix (rely on Safari matching)
excludedCVEs: []string{"CVE-2023-28205"},
},
{
platform: "darwin",
version: "11.7.6",
osID: 12,
// macOS Big Sur 11.7.6 did NOT fix CVE-2023-28205 (only fixed CVE-2023-28206)
excludedCVEs: []string{"CVE-2023-28205"},
},
{
platform: "darwin",
version: "10.15.7",
osID: 13,
// macOS Catalina (10.x) should NOT match CVE-2023-28205 (pre-Big Sur)
excludedCVEs: []string{"CVE-2023-28205"},
},
}
t.Run("find_vulns_on_cpes", func(t *testing.T) {
t.Parallel()
ds := new(mock.Store)
softwareIDToCPEs := make(map[uint]string)
ds.ListSoftwareCPEsFunc = func(ctx context.Context) ([]fleet.SoftwareCPE, error) {
var softwareCPEs []fleet.SoftwareCPE
i := uint(0)
for cpe := range cveTests {
softwareCPEs = append(softwareCPEs, fleet.SoftwareCPE{CPE: cpe, SoftwareID: i})
softwareIDToCPEs[i] = cpe
i++
}
return softwareCPEs, nil
}
var osIDs []uint
ds.ListOperatingSystemsForPlatformFunc = func(ctx context.Context, p string) ([]fleet.OperatingSystem, error) {
var oss []fleet.OperatingSystem
for _, os := range cveOSTests {
oss = append(oss, fleet.OperatingSystem{
ID: os.osID,
Platform: os.platform,
Version: os.version,
})
osIDs = append(osIDs, os.osID)
}
return oss, nil
}
cveLock := &sync.Mutex{}
cvesFound := make(map[string][]cve)
ds.InsertSoftwareVulnerabilityFunc = func(ctx context.Context, vuln fleet.SoftwareVulnerability, src fleet.VulnerabilitySource) (bool, error) {
cveLock.Lock()
defer cveLock.Unlock()
cpe, ok := softwareIDToCPEs[vuln.SoftwareID]
if !ok {
return false, fmt.Errorf("software id -> cpe not found: %d", vuln.SoftwareID)
}
cve := cve{
ID: vuln.CVE,
resolvedInVersion: *vuln.ResolvedInVersion,
}
cvesFound[cpe] = append(cvesFound[cpe], cve)
return false, nil
}
osCVELock := &sync.Mutex{}
osCVEsFound := make(map[uint][]string)
ds.InsertOSVulnerabilityFunc = func(ctx context.Context, vuln fleet.OSVulnerability, src fleet.VulnerabilitySource) (bool, error) {
osCVELock.Lock()
defer osCVELock.Unlock()
osCVEsFound[vuln.OSID] = append(osCVEsFound[vuln.OSID], vuln.CVE)
return false, nil
}
ds.DeleteOutOfDateVulnerabilitiesFunc = func(ctx context.Context, source fleet.VulnerabilitySource, olderThan time.Time) error {
return nil
}
ds.DeleteOutOfDateOSVulnerabilitiesFunc = func(ctx context.Context, source fleet.VulnerabilitySource, olderThan time.Time) error {
return nil
}
_, err := TranslateCPEToCVE(ctx, ds, tempDir, kitlog.NewNopLogger(), false, time.Now().UTC().Add(-time.Hour))
require.NoError(t, err)
require.True(t, ds.DeleteOutOfDateVulnerabilitiesFuncInvoked)
require.True(t, ds.DeleteOutOfDateOSVulnerabilitiesFuncInvoked)
for cpe, tc := range cveTests {
if tc.continuesToUpdate {
// Given that new vulnerabilities can be found on these
// packages/products, we check that at least the
// known ones are found.
for _, cve := range tc.includedCVEs {
require.Contains(t, cvesFound[cpe], cve, fmt.Sprintf("%s does not contain CVE %#v", cpe, cve))
}
} else {
// Check for exact match of CVEs found.
require.ElementsMatch(t, cvesFound[cpe], tc.includedCVEs, cpe)
}
for _, cve := range tc.excludedCVEs {
for _, cveFound := range cvesFound[cpe] {
require.NotEqual(t, cve, cveFound.ID, fmt.Sprintf("%s should not contain %s", cpe, cve))
}
}
}
for _, tc := range cveOSTests {
for _, cve := range tc.includedCVEs {
require.Contains(t, osCVEsFound[tc.osID], cve)
}
for _, cve := range tc.excludedCVEs {
require.NotContains(t, osCVEsFound[tc.osID], cve)
}
}
})
t.Run("recent_vulns", func(t *testing.T) {
t.Parallel()
ds := new(mock.Store)
safeDS := &threadSafeDSMock{Store: ds}
softwareCPEs := []fleet.SoftwareCPE{
{CPE: "cpe:2.3:a:google:chrome:*:*:*:*:*:*:*:*", ID: 1, SoftwareID: 1},
{CPE: "cpe:2.3:a:mozilla:firefox:*:*:*:*:*:*:*:*", ID: 2, SoftwareID: 2},
{CPE: "cpe:2.3:a:haxx:curl:*:*:*:*:*:*:*:*", ID: 3, SoftwareID: 3},
}
ds.DeleteOutOfDateVulnerabilitiesFunc = func(ctx context.Context, source fleet.VulnerabilitySource, olderThan time.Time) error {
return nil
}
ds.ListSoftwareCPEsFunc = func(ctx context.Context) ([]fleet.SoftwareCPE, error) {
return softwareCPEs, nil
}
ds.InsertSoftwareVulnerabilityFunc = func(ctx context.Context, vuln fleet.SoftwareVulnerability, src fleet.VulnerabilitySource) (bool, error) {
return true, nil
}
ds.ListOperatingSystemsForPlatformFunc = func(ctx context.Context, p string) ([]fleet.OperatingSystem, error) {
return nil, nil
}
ds.DeleteOutOfDateOSVulnerabilitiesFunc = func(ctx context.Context, source fleet.VulnerabilitySource, olderThan time.Time) error {
return nil
}
recent, err := TranslateCPEToCVE(ctx, safeDS, tempDir, kitlog.NewNopLogger(), true, time.Now().Add(-time.Hour))
require.NoError(t, err)
byCPE := make(map[uint]int)
for _, cpe := range recent {
byCPE[cpe.Affected()]++
}
// even if it's somewhat far in the past, I've seen the exact numbers
// change a bit between runs with different downloads, so allow for a bit
// of wiggle room.
assert.Greater(t, byCPE[softwareCPEs[0].SoftwareID], 150, "google chrome CVEs")
assert.Greater(t, byCPE[softwareCPEs[1].SoftwareID], 280, "mozilla firefox CVEs")
assert.Greater(t, byCPE[softwareCPEs[2].SoftwareID], 10, "curl CVEs")
// call it again but now return false from this call, simulating CVE-CPE pairs
// that already existed in the DB.
ds.InsertSoftwareVulnerabilityFunc = func(ctx context.Context, vuln fleet.SoftwareVulnerability, src fleet.VulnerabilitySource) (bool, error) {
return false, nil
}
recent, err = TranslateCPEToCVE(ctx, safeDS, tempDir, kitlog.NewNopLogger(), true, time.Now().UTC().Add(-time.Hour))
require.NoError(t, err)
// no recent vulnerability should be reported
assert.Len(t, recent, 0)
})
}
func TestSyncsCVEFromURL(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if strings.HasSuffix(r.RequestURI, ".meta") {
fmt.Fprint(w, "lastModifiedDate:2021-08-04T11:10:30-04:00\r\n")
fmt.Fprint(w, "size:20967174\r\n")
fmt.Fprint(w, "zipSize:1453429\r\n")
fmt.Fprint(w, "gzSize:1453293\r\n")
fmt.Fprint(w, "sha256:10D7338A1E2D8DB344C381793110B67FCA7D729ADA21624EF089EBA78CCE7B53\r\n")
}
}))
defer ts.Close()
tempDir := t.TempDir()
cveFeedPrefixURL := ts.URL + "/feeds/json/cve/1.1/"
err := DownloadCVEFeed(tempDir, cveFeedPrefixURL, false, log.NewNopLogger())
require.Error(t, err)
require.Contains(t,
err.Error(),
fmt.Sprintf("1 synchronisation error:\n\tunexpected size for \"%s/feeds/json/cve/1.1/nvdcve-1.1-2002.json.gz\" (200 OK): want 1453293, have 0", ts.URL),
)
}
// This test is using real data from the 2022 NVD feed
func TestGetMatchingVersionEndExcluding(t *testing.T) {
ctx := context.Background()
testDict := loadDict(t, "../testdata/nvdcve-1.1-2022.json.gz")
tests := []struct {
name string
cve string
meta *wfn.Attributes
want string
wantErr bool
}{
{
name: "happy path with version with no Version Start",
cve: "CVE-2022-40897",
meta: &wfn.Attributes{
Vendor: "python",
Product: "setuptools",
Version: "64",
},
want: "65.5.1",
wantErr: false,
},
{
name: "CVE matches multiple products",
cve: "CVE-2022-40956",
meta: &wfn.Attributes{
Vendor: "mozilla",
Product: "firefox",
Version: "93.0.100",
},
want: "105.0",
wantErr: false,
},
{
name: "Nodes has nested Children",
cve: "CVE-2022-40961",
meta: &wfn.Attributes{
Vendor: "mozilla",
Product: "firefox",
Version: "93.0.100",
},
want: "105.0",
wantErr: false,
},
{
name: "Multiple product matches with different version ranges",
cve: "CVE-2022-26697",
meta: &wfn.Attributes{
Vendor: "apple",
Product: "macos",
Version: "12.0",
},
want: "12.4",
wantErr: false,
},
{
name: "No version end excluding",
cve: "CVE-2022-26834",
meta: &wfn.Attributes{
Vendor: "cybozu",
Product: "remote_service_manager",
Version: "3.1.2",
},
want: "",
wantErr: false,
},
{
name: "Version exceeds version end excluding",
cve: "CVE-2022-4610",
meta: &wfn.Attributes{
Vendor: "clickstudios",
Product: "passwordstate",
Version: "9.5.8.4",
TargetSW: "chrome",
},
want: "",
wantErr: false,
},
{
name: "Can compare 4th version part",
cve: "CVE-2022-45889",
meta: &wfn.Attributes{
Vendor: "planetestream",
Product: "planet_estream",
Version: "6.72.10.06",
},
want: "6.72.10.07",
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := getMatchingVersionEndExcluding(ctx, tt.cve, tt.meta, testDict, nil)
if (err != nil) != tt.wantErr {
t.Errorf("getMatchingVersionEndExcluding() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("got %q, want %q", got, tt.want)
}
})
}
}
func TestGetMacOSCPEs(t *testing.T) {
ctx := context.Background()
ds := new(mock.Store)
os := fleet.OperatingSystem{
ID: 1,
Name: "macOS",
Version: "11.6.2",
Arch: "x86_64",
KernelVersion: "20.6.0",
Platform: "darwin",
}
ds.ListOperatingSystemsForPlatformFunc = func(ctx context.Context, p string) ([]fleet.OperatingSystem, error) {
return []fleet.OperatingSystem{os}, nil
}
CVEs, err := GetMacOSCPEs(ctx, ds)
require.NoError(t, err)
require.Len(t, CVEs, 2)
expected := map[osCPEWithNVDMeta]struct{}{
{
OperatingSystem: os,
meta: &wfn.Attributes{
Part: "o",
Vendor: "apple",
Product: "mac_os_x",
Version: CVEs[0].Version,
Update: wfn.Any,
Edition: wfn.Any,
SWEdition: wfn.Any,
TargetSW: wfn.Any,
TargetHW: wfn.Any,
Other: wfn.Any,
Language: wfn.Any,
},
}: {},
{
OperatingSystem: os,
meta: &wfn.Attributes{
Part: "o",
Vendor: "apple",
Product: "macos",
Version: CVEs[0].Version,
Update: wfn.Any,
Edition: wfn.Any,
SWEdition: wfn.Any,
TargetSW: wfn.Any,
TargetHW: wfn.Any,
Other: wfn.Any,
Language: wfn.Any,
},
}: {},
}
for _, cve := range CVEs {
require.Contains(t, expected, cve)
}
}
// loadDict loads a cvefeed.Dictionary from a JSON NVD feed file.
func loadDict(t *testing.T, path string) cvefeed.Dictionary {
dict, err := cvefeed.LoadJSONDictionary(path)
if err != nil {
t.Fatal(err)
}
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"
python3140Alpha2 := &wfn.Attributes{
Vendor: "python",
Product: "python",
Version: "3.14.0",
Update: "alpha2",
TargetSW: "windows",
}
python3140Alpha2Alias1 := *python3140Alpha2
python3140Alpha2Alias1.Version = "3.14.0a2"
python3140Alpha2Alias1.Update = ""
python3130Beta1 := &wfn.Attributes{
Vendor: "python",
Product: "python",
Version: "3.13.0",
Update: "beta2",
TargetSW: "windows",
}
python3130Beta1Alias := *python3130Beta1
python3130Beta1Alias.Version = "3.13.0b2"
python3130Beta1Alias.Update = ""
python3130RC1 := &wfn.Attributes{
Vendor: "python",
Product: "python",
Version: "3.13.0",
Update: "rc1",
TargetSW: "windows",
}
python3130RC1Alias := *python3130RC1
python3130RC1Alias.Version = "3.13.0rc1"
python3130RC1Alias.Update = ""
pgadminMacOS := &wfn.Attributes{
Vendor: "pgadmin",
Product: "pgadmin",
Version: "9.10",
TargetSW: "macos",
}
pgadminMacOSAliasPgadmin := *pgadminMacOS
pgadminMacOSAliasPgadmin.TargetSW = "postgresql"
pgadminMacOSAliasPgadmin_4 := *pgadminMacOS
pgadminMacOSAliasPgadmin_4.Product = "pgadmin_4"
pgadminMacOSAliasPgadmin_4.TargetSW = "postgresql"
pgadminMacOSAliasPgadmin4 := *pgadminMacOS
pgadminMacOSAliasPgadmin4.Product = "pgadmin4"
pgadminMacOSAliasPgadmin4.TargetSW = "postgresql"
pgadminWindows := &wfn.Attributes{
Vendor: "pgadmin",
Product: "pgadmin_4",
Version: "9.10",
TargetSW: "windows",
}
pgadminWindowsAliasPgadmin := *pgadminWindows
pgadminWindowsAliasPgadmin.Product = "pgadmin"
pgadminWindowsAliasPgadmin.TargetSW = "postgresql"
pgadminWindowsAliasPgadmin_4 := *pgadminWindows
pgadminWindowsAliasPgadmin_4.TargetSW = "postgresql"
pgadminWindowsAliasPgadmin4 := *pgadminWindows
pgadminWindowsAliasPgadmin4.Product = "pgadmin4"
pgadminWindowsAliasPgadmin4.TargetSW = "postgresql"
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},
},
{
name: "pre-release python: 3.14.0 alpha2",
cpeItem: python3140Alpha2,
expectedAliases: []*wfn.Attributes{python3140Alpha2, &python3140Alpha2Alias1},
},
{
name: "pre-release python: 3.13.0 beta1",
cpeItem: python3130Beta1,
expectedAliases: []*wfn.Attributes{python3130Beta1, &python3130Beta1Alias},
},
{
name: "pre-release python: 3.13.0 rc1",
cpeItem: python3130RC1,
expectedAliases: []*wfn.Attributes{python3130RC1, &python3130RC1Alias},
},
{
name: "pgadmin on macos",
cpeItem: pgadminMacOS,
expectedAliases: []*wfn.Attributes{
pgadminMacOS,
&pgadminMacOSAliasPgadmin,
&pgadminMacOSAliasPgadmin_4,
&pgadminMacOSAliasPgadmin4,
},
},
{
name: "pgadmin on windows",
cpeItem: pgadminWindows,
expectedAliases: []*wfn.Attributes{
pgadminWindows,
&pgadminWindowsAliasPgadmin,
&pgadminWindowsAliasPgadmin_4,
&pgadminWindowsAliasPgadmin4,
},
},
} {
t.Run(tc.name, func(t *testing.T) {
aliases := expandCPEAliases(tc.cpeItem)
require.Equal(t, tc.expectedAliases, aliases)
})
}
}