package dbstore import ( "compress/gzip" "context" db_sql "database/sql" "encoding/json" "encoding/xml" "embed" "errors" "fmt" _ "github.com/mattn/go-sqlite3" "github.com/pablotron/cvez/cpedict" "github.com/pablotron/cvez/cpematch" nvd_feed "github.com/pablotron/cvez/feed" io_fs "io/fs" "os" "path/filepath" "reflect" "testing" "time" ) // Load gzip-compressed test feed. func getFeed(path string) (nvd_feed.Feed, error) { var feed nvd_feed.Feed // open file for reading file, err := os.Open(path) if err != nil { return feed, err } // wrap in reader, return success src, err := gzip.NewReader(file) if err != nil { return feed, err } // create decoder, decode feed, return result d := json.NewDecoder(src) return feed, d.Decode(&feed) } func getTestDictionary(path string) (cpedict.Dictionary, error) { var dict cpedict.Dictionary // open test data f, err := os.Open(path) if err != nil { return dict, err } defer f.Close() // create zip reader gz, err := gzip.NewReader(f) if err != nil { return dict, err } defer gz.Close() // create xml decoder d := xml.NewDecoder(gz) // decode xml if err = d.Decode(&dict); err != nil { return dict, err } // return success return dict, nil } //go:embed testdata/sql/*.sql var testSqlFs embed.FS var testSqlIds = map[string]bool { "init": false, "insert-cpe": true, "insert-title": true, "insert-ref": true, } func getTestQueries() (map[string]string, error) { r := make(map[string]string) for id, _ := range(testSqlIds) { path := fmt.Sprintf("testdata/sql/%s.sql", id) if data, err := testSqlFs.ReadFile(path); err != nil { return r, err } else { r[id] = string(data) } } return r, nil } func ignoreTestSimple(t *testing.T) { testDbPath := "./testdata/foo.db" // get queries queries, err := getTestQueries() if err != nil { t.Error(err) return } // load test CPEs dict, err := getTestDictionary("testdata/test-0.xml.gz") if err != nil { t.Error(err) return } // does test db exist? if _, err = os.Stat(testDbPath); err != nil { if !errors.Is(err, io_fs.ErrNotExist) { t.Error(err) return } } else if err == nil { // remove test db if err = os.Remove(testDbPath); err != nil { t.Error(err) return } } // init db db, err := db_sql.Open("sqlite3", testDbPath) if err != nil { t.Error(err) return } defer db.Close() // init tables if _, err := db.Exec(queries["init"]); err != nil { t.Error(err) return } tx, err := db.Begin() if err != nil { t.Error(err) return } // build statements sts := make(map[string]*db_sql.Stmt) for id, use := range(testSqlIds) { if use { if st, err := tx.Prepare(queries[id]); err != nil { t.Error(err) return } else { sts[id] = st defer sts[id].Close() } } } // add items for _, item := range(dict.Items) { // add cpe rs, err := sts["insert-cpe"].Exec(item.CpeUri, item.Cpe23Item.Name); if err != nil { t.Error(err) return } // get last row ID id, err := rs.LastInsertId() if err != nil { t.Error(err) return } // add titles for _, title := range(item.Titles) { if _, err := sts["insert-title"].Exec(id, title.Lang, title.Text); err != nil { t.Error(err) return } } // add refs for _, ref := range(item.References) { if _, err := sts["insert-ref"].Exec(id, ref.Href, ref.Text); err != nil { t.Error(err) return } } } // commit changes if err = tx.Commit(); err != nil { t.Error(err) return } } func createTestDb(ctx context.Context, path string) (DbStore, error) { // remove existing file err := os.Remove(path) if err != nil && !errors.Is(err, io_fs.ErrNotExist) { return DbStore{}, err } // open db return Open(path) } func seedTestDb(ctx context.Context, db DbStore) error { // load test CPEs dict, err := getTestDictionary("testdata/test-0.xml.gz") if err != nil { return err } // add cpe dictionary return db.AddCpeDictionary(ctx, dict) // TODO: seed with other data } func TestOpenFull(t *testing.T) { tests := []struct { name string dbType string path string exp bool } { { "pass", "sqlite3", "./testdata/test-open.db", true }, { "fail", "invalid driver", "file://invalid/foobar", false }, } for _, test := range(tests) { t.Run(test.name, func(t *testing.T) { got, err := openFull(test.dbType, test.path) if test.exp && err != nil { t.Error(err) } else if !test.exp && err == nil { t.Errorf("got %v, exp error", got) } }) } } func TestInitFail(t *testing.T) { // set deadline to 2 hours ago deadline := time.Now().Add(-2 * time.Hour) ctx, _ := context.WithDeadline(context.Background(), deadline) db, err := createTestDb(ctx, "./testdata/test-init-fail.db") if err != nil { t.Errorf("createTestDb(): got %v, exp error", db) } if err = db.Init(ctx); err == nil { t.Errorf("Init(): got %v, exp error", db) } } func TestAddCpeDictionaryPass(t *testing.T) { if testing.Short() { t.Skip("skipping TestAddCveFeeds() in short mode") return } path := "./testdata/test-addcpedict.db" ctx := context.Background() // create db db, err := createTestDb(ctx, path) if err != nil { t.Error(err) return } // load test CPEs dict, err := getTestDictionary("testdata/test-0.xml.gz") if err != nil { t.Error(err) return } // add cpe dictionary if err := db.AddCpeDictionary(ctx, dict); err != nil { t.Error(err) return } } func TestAddCpeDictionaryFail(t *testing.T) { if testing.Short() { t.Skip("skipping TestAddCveFeeds() in short mode") return } // load test CPEs dict, err := getTestDictionary("testdata/test-0.xml.gz") if err != nil { t.Error(err) return } funcTests := []struct { name string fn func(string) func(*testing.T) } {{ name: "deadline", fn: func(path string) func(*testing.T) { return func(t *testing.T) { deadline := time.Now().Add(-2 * time.Hour) ctx, _ := context.WithDeadline(context.Background(), deadline) // create db db, err := createTestDb(ctx, path) if err != nil { t.Error(err) return } // add cpe dictionary if err := db.AddCpeDictionary(ctx, dict); err == nil { t.Errorf("got success, exp error") } } }, }, { name: "tx", fn: func(path string) func(*testing.T) { return func(t *testing.T) { ctx := context.Background() // create db db, err := createTestDb(ctx, path) if err != nil { t.Error(err) return } // begin transaction if _, err = db.db.BeginTx(ctx, nil); err != nil { t.Error(err) return } // FIXME: busted // // add cpe dictionary // if err := db.AddCpeDictionary(ctx, dict); err == nil { // t.Errorf("got success, exp error") // } } }, }} for _, test := range(funcTests) { path := fmt.Sprintf("./testdata/test-addcpedict-fail-%s.db", test.name) t.Run(test.name, test.fn(path)) } dictTests := []struct { name string dict cpedict.Dictionary } {{ name: "bad-cpe23", dict: cpedict.Dictionary { Items: []cpedict.Item { cpedict.Item{} }, }, }, { name: "bad-title", dict: cpedict.Dictionary { Items: []cpedict.Item { cpedict.Item { CpeUri: "cpe:/a", Cpe23Item: cpedict.Cpe23Item { Name: "cpe:2.3:*:*:*:*:*:*:*:*:*:*:*", }, Titles: []cpedict.Title { cpedict.Title {}, }, }, }, }, }, { name: "bad-ref", dict: cpedict.Dictionary { Items: []cpedict.Item { cpedict.Item { CpeUri: "cpe:/a", Cpe23Item: cpedict.Cpe23Item { Name: "cpe:2.3:*:*:*:*:*:*:*:*:*:*:*", }, Titles: []cpedict.Title { cpedict.Title { Lang: "en-US", Text: "foo" }, }, References: []cpedict.Reference { cpedict.Reference {}, }, }, }, }, }} for _, test := range(dictTests) { t.Run(test.name, func(t *testing.T) { ctx := context.Background() path := fmt.Sprintf("./testdata/test-addcpedict-fail-%s.db", test.name) // create db db, err := createTestDb(ctx, path) if err != nil { t.Error(err) return } // add cpe dictionary if err := db.AddCpeDictionary(ctx, test.dict); err == nil { t.Errorf("got success, exp error") } }) } } func TestCpeSearch(t *testing.T) { path := "./testdata/test-search.db" ctx := context.Background() // tests that are expected to pass passTests := []struct { t CpeSearchType // search type q string // query string exp []string // expected search results (cpe23s) } {{ t: CpeSearchAll, q: "advisory AND book", exp: []string { "cpe:2.3:a:\\$0.99_kindle_books_project:\\$0.99_kindle_books:6:*:*:*:*:android:*:*", }, }, { t: CpeSearchTitle, q: "project", exp: []string { "cpe:2.3:a:\\@thi.ng\\/egf_project:\\@thi.ng\\/egf:-:*:*:*:*:node.js:*:*", "cpe:2.3:a:\\@thi.ng\\/egf_project:\\@thi.ng\\/egf:0.1.0:*:*:*:*:node.js:*:*", "cpe:2.3:a:\\@thi.ng\\/egf_project:\\@thi.ng\\/egf:0.2.0:*:*:*:*:node.js:*:*", "cpe:2.3:a:\\@thi.ng\\/egf_project:\\@thi.ng\\/egf:0.2.1:*:*:*:*:node.js:*:*", "cpe:2.3:a:\\$0.99_kindle_books_project:\\$0.99_kindle_books:6:*:*:*:*:android:*:*", }, }, { t: CpeSearchRef, q: "advisory", exp: []string { "cpe:2.3:a:\\@thi.ng\\/egf_project:\\@thi.ng\\/egf:-:*:*:*:*:node.js:*:*", "cpe:2.3:a:\\@thi.ng\\/egf_project:\\@thi.ng\\/egf:0.1.0:*:*:*:*:node.js:*:*", "cpe:2.3:a:\\@thi.ng\\/egf_project:\\@thi.ng\\/egf:0.2.0:*:*:*:*:node.js:*:*", "cpe:2.3:a:\\@thi.ng\\/egf_project:\\@thi.ng\\/egf:0.2.1:*:*:*:*:node.js:*:*", "cpe:2.3:a:360totalsecurity:360_total_security:12.1.0.1005:*:*:*:*:*:*:*", "cpe:2.3:a:\\$0.99_kindle_books_project:\\$0.99_kindle_books:6:*:*:*:*:android:*:*", }, }} // create db db, err := createTestDb(ctx, path) if err != nil { t.Error(err) return } // seed test database if err = seedTestDb(ctx, db); err != nil { t.Error(err) return } for _, test := range(passTests) { t.Run(test.t.String(), func(t *testing.T) { rows, err := db.CpeSearch(ctx, test.t, test.q) if err != nil { t.Error(err) return } // build ids got := make([]string, len(rows)) for i, row := range(rows) { got[i] = row.Cpe23 } if !reflect.DeepEqual(got, test.exp) { t.Errorf("got \"%v\", exp \"%v\"", got, test.exp) return } }) } // tests that are expected to fail failTests := []struct { name string // test name t CpeSearchType // search type q string // query string } { { "bad-search-type", CpeSearchType(255), "" }, } for _, test := range(failTests) { t.Run(test.name, func(t *testing.T) { if got, err := db.CpeSearch(ctx, test.t, test.q); err == nil { t.Errorf("got \"%v\", exp error", got) } }) } } func TestAddCpeMatches(t *testing.T) { if testing.Short() { t.Skip("skipping TestAddCveFeeds() in short mode") return } // cache context, create temp dir ctx := context.Background() dir, err := os.MkdirTemp("", "") if err != nil { t.Error(err) return } defer os.RemoveAll(dir) vuln := true passTests := []struct { name string // test name and path seed string // db seed query matches []cpematch.Match // matches } {{ name: "pass-basic", seed: ` INSERT INTO cpes(cpe_uri, cpe23) VALUES ( 'cpe:/1', 'cpe:2.3:a:101_project:101:1.0.0:*:*:*:*:node.js:*:*' ), ( 'cpe:/2', 'cpe:2.3:a:101_project:101:1.1.0:*:*:*:*:node.js:*:*' ), ( 'cpe:/3', 'cpe:2.3:a:101_project:101:1.1.1:*:*:*:*:node.js:*:*' ); `, matches: []cpematch.Match { cpematch.Match { Cpe23Uri: "cpe:2.3:a:101_project:101:*:*:*:*:*:node.js:*:*", Vulnerable: &vuln, VersionStartIncluding: "1.0.0", VersionEndIncluding: "1.6.3", Names: []cpematch.Name { cpematch.Name { Cpe23Uri: "cpe:2.3:a:101_project:101:1.0.0:*:*:*:*:node.js:*:*", }, cpematch.Name { Cpe23Uri: "cpe:2.3:a:101_project:101:1.1.0:*:*:*:*:node.js:*:*", }, cpematch.Name { Cpe23Uri: "cpe:2.3:a:101_project:101:1.1.1:*:*:*:*:node.js:*:*", }, }, }, }, }, { name: "pass-excluding", seed: ` INSERT INTO cpes(cpe_uri, cpe23) VALUES ( 'cpe:/1', 'cpe:2.3:a:101_project:101:1.0.0:*:*:*:*:node.js:*:*' ), ( 'cpe:/2', 'cpe:2.3:a:101_project:101:1.1.0:*:*:*:*:node.js:*:*' ), ( 'cpe:/3', 'cpe:2.3:a:101_project:101:1.1.1:*:*:*:*:node.js:*:*' ); `, matches: []cpematch.Match { cpematch.Match { Cpe23Uri: "cpe:2.3:a:101_project:101:*:*:*:*:*:node.js:*:*", Vulnerable: &vuln, VersionStartExcluding: "1.0.0", VersionEndExcluding: "1.6.3", Names: []cpematch.Name { cpematch.Name { Cpe23Uri: "cpe:2.3:a:101_project:101:1.0.0:*:*:*:*:node.js:*:*", }, cpematch.Name { Cpe23Uri: "cpe:2.3:a:101_project:101:1.1.0:*:*:*:*:node.js:*:*", }, cpematch.Name { Cpe23Uri: "cpe:2.3:a:101_project:101:1.1.1:*:*:*:*:node.js:*:*", }, }, }, }, }} for _, test := range(passTests) { t.Run(test.name, func(t *testing.T) { // build test matches matches := cpematch.Matches { Matches: test.matches } // open db db, err := Open(filepath.Join(dir, fmt.Sprintf("%s.db", test.name))) if err != nil { t.Error(err) return } // init db if err := db.Init(ctx); err != nil { t.Error(err) return } // seed db if _, err = db.db.ExecContext(ctx, test.seed); err != nil { t.Error(err) return } // add matches if err = db.AddCpeMatches(ctx, matches); err != nil { t.Error(err) } }) } failTests := []struct { name string // test name (path inferred) seed string // db seed query matches []cpematch.Match // matches } {{ name: "bad-match-cpe23", seed: ` INSERT INTO cpes(cpe_uri, cpe23) VALUES ( 'cpe:/1', 'cpe:2.3:a:101_project:101:1.0.0:*:*:*:*:node.js:*:*' ), ( 'cpe:/2', 'cpe:2.3:a:101_project:101:1.1.0:*:*:*:*:node.js:*:*' ), ( 'cpe:/3', 'cpe:2.3:a:101_project:101:1.1.1:*:*:*:*:node.js:*:*' ); `, matches: []cpematch.Match { cpematch.Match { Cpe23Uri: "cpe:", VersionStartIncluding: "1.0.0", VersionEndIncluding: "1.6.3", Names: []cpematch.Name { cpematch.Name { Cpe23Uri: "cpe:2.3:a:101_project:101:1.0.0:*:*:*:*:node.js:*:*", }, cpematch.Name { Cpe23Uri: "cpe:2.3:a:101_project:101:1.1.0:*:*:*:*:node.js:*:*", }, cpematch.Name { Cpe23Uri: "cpe:2.3:a:101_project:101:1.1.1:*:*:*:*:node.js:*:*", }, }, }, }, }, { name: "bad-cpe", seed: ` INSERT INTO cpes(cpe_uri, cpe23) VALUES ( 'cpe:/1', 'cpe:2.3:a:101_project:101:1.0.0:*:*:*:*:node.js:*:*' ), ( 'cpe:/2', 'cpe:2.3:a:101_project:101:1.1.0:*:*:*:*:node.js:*:*' ), ( 'cpe:/3', 'cpe:2.3:a:101_project:101:1.1.1:*:*:*:*:node.js:*:*' ); `, matches: []cpematch.Match { cpematch.Match { Cpe23Uri: "cpe:2.3:a:101_project:101:*:*:*:*:*:node.js:*:*", VersionStartIncluding: "1.0.0", VersionEndIncluding: "1.6.3", Names: []cpematch.Name { cpematch.Name { Cpe23Uri: "cpe:2.3", }, cpematch.Name { Cpe23Uri: "cpe:2.3:a:101_project:101:1.1.0:*:*:*:*:node.js:*:*", }, cpematch.Name { Cpe23Uri: "cpe:2.3:a:101_project:101:1.1.1:*:*:*:*:node.js:*:*", }, }, }, }, }, { name: "dup-versionstart", seed: ` INSERT INTO cpes(cpe_uri, cpe23) VALUES ( 'cpe:/1', 'cpe:2.3:a:101_project:101:1.0.0:*:*:*:*:node.js:*:*' ), ( 'cpe:/2', 'cpe:2.3:a:101_project:101:1.1.0:*:*:*:*:node.js:*:*' ), ( 'cpe:/3', 'cpe:2.3:a:101_project:101:1.1.1:*:*:*:*:node.js:*:*' ); `, matches: []cpematch.Match { cpematch.Match { Cpe23Uri: "cpe:2.3:a:101_project:101:*:*:*:*:*:node.js:*:*", VersionStartIncluding: "1.0.0", VersionStartExcluding: "1.1.0", Names: []cpematch.Name { cpematch.Name { Cpe23Uri: "cpe:2.3:a:101_project:101:1.0.0:*:*:*:*:node.js:*:*", }, cpematch.Name { Cpe23Uri: "cpe:2.3:a:101_project:101:1.1.0:*:*:*:*:node.js:*:*", }, cpematch.Name { Cpe23Uri: "cpe:2.3:a:101_project:101:1.1.1:*:*:*:*:node.js:*:*", }, }, }, }, }, { name: "dup-versionend", seed: ` INSERT INTO cpes(cpe_uri, cpe23) VALUES ( 'cpe:/1', 'cpe:2.3:a:101_project:101:1.0.0:*:*:*:*:node.js:*:*' ), ( 'cpe:/2', 'cpe:2.3:a:101_project:101:1.1.0:*:*:*:*:node.js:*:*' ), ( 'cpe:/3', 'cpe:2.3:a:101_project:101:1.1.1:*:*:*:*:node.js:*:*' ); `, matches: []cpematch.Match { cpematch.Match { Cpe23Uri: "cpe:2.3:a:101_project:101:*:*:*:*:*:node.js:*:*", VersionEndIncluding: "1.0.0", VersionEndExcluding: "1.1.0", Names: []cpematch.Name { cpematch.Name { Cpe23Uri: "cpe:2.3:a:101_project:101:1.0.0:*:*:*:*:node.js:*:*", }, cpematch.Name { Cpe23Uri: "cpe:2.3:a:101_project:101:1.1.0:*:*:*:*:node.js:*:*", }, cpematch.Name { Cpe23Uri: "cpe:2.3:a:101_project:101:1.1.1:*:*:*:*:node.js:*:*", }, }, }, }, }} for _, test := range(failTests) { t.Run(test.name, func(t *testing.T) { // build test matches matches := cpematch.Matches { Matches: test.matches } // open db db, err := Open(filepath.Join(dir, fmt.Sprintf("%s.db", test.name))) if err != nil { t.Error(err) return } // init db if err := db.Init(ctx); err != nil { t.Error(err) return } // seed db if _, err = db.db.ExecContext(ctx, test.seed); err != nil { t.Error(err) return } // add matches if err = db.AddCpeMatches(ctx, matches); err == nil { t.Error("got success, exp error") } }) } } func TestCpeMatchSearch(t *testing.T) { // cache context, create temp dir ctx := context.Background() dir, err := os.MkdirTemp("", "") if err != nil { t.Error(err) return } defer os.RemoveAll(dir) // db cpe seed query seed := ` INSERT INTO cpes(cpe_uri, cpe23) VALUES ( 'cpe:/1', 'cpe:2.3:a:101_project:101:1.0.0:*:*:*:*:node.js:*:*' ), ( 'cpe:/2', 'cpe:2.3:a:101_project:101:1.1.0:*:*:*:*:node.js:*:*' ), ( 'cpe:/3', 'cpe:2.3:a:101_project:101:1.1.1:*:*:*:*:node.js:*:*' ); ` matches := cpematch.Matches { Matches: []cpematch.Match { cpematch.Match { Cpe23Uri: "cpe:2.3:a:101_project:101:*:*:*:*:*:node.js:*:*", VersionStartIncluding: "1.0.0", VersionEndIncluding: "1.6.3", Names: []cpematch.Name { cpematch.Name { Cpe23Uri: "cpe:2.3:a:101_project:101:1.0.0:*:*:*:*:node.js:*:*", }, cpematch.Name { Cpe23Uri: "cpe:2.3:a:101_project:101:1.1.0:*:*:*:*:node.js:*:*", }, cpematch.Name { Cpe23Uri: "cpe:2.3:a:101_project:101:1.1.1:*:*:*:*:node.js:*:*", }, }, }, }, } // build test matches // open db db, err := Open(filepath.Join(dir, "TestCpeMatchSearch.db")) if err != nil { t.Error(err) return } // init db if err := db.Init(ctx); err != nil { t.Error(err) return } // seed db if _, err = db.db.ExecContext(ctx, seed); err != nil { t.Error(err) return } // add matches if err = db.AddCpeMatches(ctx, matches); err != nil { t.Error(err) return } tests := []struct { val string // search val exp []string // expected results } {{ val: "nothing", exp: []string {}, }, { val: "cpe:2.3:a:101_project:101:*:*:*:*:*:node.js:*:*", exp: []string { "cpe:2.3:a:101_project:101:1.0.0:*:*:*:*:node.js:*:*", "cpe:2.3:a:101_project:101:1.1.0:*:*:*:*:node.js:*:*", "cpe:2.3:a:101_project:101:1.1.1:*:*:*:*:node.js:*:*", }, }} for _, test := range(tests) { t.Run(test.val, func(t *testing.T) { if got, err := db.CpeMatchSearch(ctx, test.val); err != nil { t.Error(err) } else if ((len(got) > 0) || (len(test.exp) > 0)) && !reflect.DeepEqual(got, test.exp) { t.Errorf("got %v, exp %v", got, test.exp) } }) } } func TestAddCveFeeds(t *testing.T) { ctx := context.Background() if testing.Short() { t.Skip("skipping TestAddCveFeeds() in short mode") return } tests := []string { "nvdcve-1.1-2002", "nvdcve-1.1-2003", "nvdcve-1.1-2021", } // create test db db, err := createTestDb(ctx, "testdata/test-addcvefeed.db") if err != nil { t.Error(err) return } // load feeds feeds := make([]nvd_feed.Feed, len(tests)) for i, test := range(tests) { // get feed path path := fmt.Sprintf("testdata/%s.json.gz", test) // get feed if feed, err := getFeed(path); err != nil { t.Error(err) return } else { feeds[i] = feed } } // add feed if _, err = db.AddCveFeeds(ctx, feeds); err != nil { t.Error(err) } } func getTestCveId(t *testing.T, s string) nvd_feed.CveId { r, err := nvd_feed.NewCveId(s) if err != nil { // raise test error t.Error(err) } return r } func TestCveSearch(t *testing.T) { ctx := context.Background() tests := []struct { val string exp []CveSearchRow } {{ val: "cisco anyconnect ipc dll mobility secure", exp: []CveSearchRow { CveSearchRow { Id: 9315, NvdId: getTestCveId(t, "CVE-2021-1366"), Description: "A vulnerability in the interprocess communication (IPC) channel of Cisco AnyConnect Secure Mobility Client for Windows could allow an authenticated, local attacker to perform a DLL hijacking attack on an affected device if the VPN Posture (HostScan) Module is installed on the AnyConnect client. This vulnerability is due to insufficient validation of resources that are loaded by the application at run time. An attacker could exploit this vulnerability by sending a crafted IPC message to the AnyConnect process. A successful exploit could allow the attacker to execute arbitrary code on the affected machine with SYSTEM privileges. To exploit this vulnerability, the attacker needs valid credentials on the Windows system.", V3BaseScore: nvd_feed.Score(uint8(78)), V3Severity: nvd_feed.SeverityHigh, Rank: -37.51353, }, CveSearchRow { Id: 9495, NvdId: getTestCveId(t, "CVE-2021-1567"), Description: "A vulnerability in the DLL loading mechanism of Cisco AnyConnect Secure Mobility Client for Windows could allow an authenticated, local attacker to perform a DLL hijacking attack on an affected device if the VPN Posture (HostScan) Module is installed on the AnyConnect client. This vulnerability is due to a race condition in the signature verification process for DLL files that are loaded on an affected device. An attacker could exploit this vulnerability by sending a series of crafted interprocess communication (IPC) messages to the AnyConnect process. A successful exploit could allow the attacker to execute arbitrary code on the affected device with SYSTEM privileges. To exploit this vulnerability, the attacker must have valid credentials on the Windows system.", V3BaseScore: nvd_feed.Score(uint8(67)), V3Severity: nvd_feed.SeverityMedium, Rank: -35.5376, }, }, }} // open test db db, err := Open("testdata/cvesearch-test.db") if err != nil { t.Error(err) return } // run tests for _, test := range(tests) { t.Run(test.val, func(t *testing.T) { got, err := db.CveSearch(ctx, test.val) if err != nil { t.Error(err) } else if !reflect.DeepEqual(got, test.exp) { t.Errorf("got \"%v\", exp \"%v\"", got, test.exp) } }) } }