Repository: AppThreat/vulnerability-db (language: python)

Issue:
[v6] Format for vulnerability database
We currently use CVE 4.0 format for the vulnerability database.

https://github.com/AppThreat/vulnerability-db/blob/master/vdb/lib/config.py#L40

CVE v5 is out with improved fields

https://github.com/CVEProject/cve-schema/tree/master/schema/v5.0

CycloneDX 1.6 is also a candidate. With cdx, we can capture the bom, evidence, and vulnerabilities together for a rich analysis although this might complicate the usage and increase the DB size.
[v6] track git vulnerabilities for nvd
If an NVD entry has references to git, we could create new entries to enable git-based searches. This would help with projects such as libexpat. 


Discussion / hints:

Fix patch:
diff --git a/README.md b/README.md
index 0eef742..8e8a5ba 100644
--- a/README.md
+++ b/README.md
@@ -37,7 +37,7 @@ pip install appthreat-vulnerability-db
 
 ## Usage
 
-This package is ideal as a library for managing vulnerabilities. This is used by [dep-scan](http://github.com/AppThreat/dep-scan), a free open-source dependency audit tool. However, there is a limited cli capability available with few features to test this tool directly.
+This package is ideal as a library for managing vulnerabilities. This is used by [owasp-dep-scan](http://github.com/owasp-dep-scan/dep-scan), a free open-source dependency audit tool. However, there is a limited cli capability available with few features to test this tool directly.
 
 ### Download pre-built database
 
@@ -52,7 +52,7 @@ oras pull ghcr.io/appthreat/vdb:v5 -o $VDB_HOME
 
 Cache application vulnerabilities
 
-```bash
+```shell
 vdb --cache
 ```
 
@@ -60,7 +60,7 @@ Typical size of this database is over 1.1 GB.
 
 Cache application and OS vulnerabilities
 
-```bash
+```shell
 vdb --cache-os
 ```
 
@@ -68,7 +68,7 @@ Note the size of the database with OS vulnerabilities is over 3.1 GB.
 
 Cache from just [OSV](https://osv.dev)
 
-```bash
+```shell
 vdb --cache --only-osv
 ```
 
@@ -77,31 +77,24 @@ It is possible to customise the cache behaviour by increasing the historic data
 - NVD_START_YEAR - Default: 2018. Supports upto 2002
 - GITHUB_PAGE_COUNT - Default: 2. Supports upto 20
 
-### Periodic sync
-
-To periodically sync the latest vulnerabilities and update the database cache.
-
-```bash
-vdb --sync
-```
-
 ### Basic search
 
 It is possible to perform simple search using the cli.
 
-```bash
-vdb --search android:8.0
-
-vdb --search google:android:8.0
-
-vdb --search android:8.0,simplesamlphp:1.14.11
-
+```shell
 vdb --search pkg:pypi/xml2dict@0.2.2
 
 # Full url and short form for swift
 vdb --search "pkg:swift/github.com/vapor/vapor@4.39.0"
 
 vdb --search "pkg:swift/vapor/vapor@4.89.0"
-```
 
-Syntax is package:version,package:version or vendor : package : version (Without space)
+# Search by cpe
+vdb --search "cpe:2.3:a:npm:gitblame:*:*:*:*:*:*:*:*"
+
+# Search by colon separated values
+vdb --search "npm:gitblame:0.0.1"
+
+# Search by CVE id
+vdb --search CVE-2024-25169
+```
diff --git a/pyproject.toml b/pyproject.toml
index 49d9ae9..dac6214 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,20 +1,20 @@
 [project]
 name = "appthreat-vulnerability-db"
 version = "6.0.0"
-description = "AppThreat's vulnerability database and package search library with a built-in file based storage. OSV, CVE, GitHub, npm are the primary sources of vulnerabilities."
+description = "AppThreat's vulnerability database and package search library with a built-in sqlite based storage. OSV, CVE, GitHub, npm are the primary sources of vulnerabilities."
 authors = [
     {name = "Team AppThreat", email = "cloud@appthreat.com"},
 ]
 dependencies = [
     "httpx[http2]",
     "appdirs",
-    "tabulate",
-    "msgpack==1.0.5",
     "orjson",
     "semver>=3.0.0",
     "packageurl-python",
     "cvss",
-    "pydantic[email]"
+    "pydantic[email]",
+    "rich",
+    "apsw>=3.45.2.0"
 ]
 requires-python = ">=3.10"
 readme = "README.md"
@@ -47,12 +47,13 @@ dev = [
     "black",
     "bandit",
     "flake8",
+    "pylint",
     "pytest",
     "pytest-cov"
 ]
 
 [tool.setuptools]
-packages = ["test", "vdb", "vdb.lib"]
+packages = ["vdb", "vdb.lib", "vdb.lib.cve_model"]
 
 [tool.pytest.ini_options]
 addopts="--showlocals -v --cov-report=term-missing --no-cov-on-fail --cov vdb"
diff --git a/vdb/cli.py b/vdb/cli.py
index 855a98b..1d1bcf6 100644
--- a/vdb/cli.py
+++ b/vdb/cli.py
@@ -4,18 +4,20 @@
 import argparse
 import logging
 import os
-import re
 import shutil
 
-from tabulate import tabulate
+import orjson
+from rich.console import Console
+from rich.syntax import Syntax
+from rich.table import Table
 
-from vdb.lib import config
-from vdb.lib import db as db_lib
+from vdb.lib import config, db6 as db_lib, search
 from vdb.lib.aqua import AquaSource
 from vdb.lib.gha import GitHubSource
-from vdb.lib.npm import NpmSource
 from vdb.lib.osv import OSVSource
 
+console = Console()
+
 logging.basicConfig(
     level=logging.INFO, format="%(levelname)s [%(asctime)s] %(message)s"
 )
@@ -24,14 +26,10 @@
     logging.getLogger(_).disabled = True
 
 AT_LOGO = """
-  ___            _____ _                    _
- / _ \          |_   _| |                  | |
-/ /_\ \_ __  _ __ | | | |__  _ __ ___  __ _| |_
-|  _  | '_ \| '_ \| | | '_ \| '__/ _ \/ _` | __|
-| | | | |_) | |_) | | | | | | | |  __/ (_| | |_
-\_| |_/ .__/| .__/\_/ |_| |_|_|  \___|\__,_|\__|
-      | |   | |
-      |_|   |_|
+             ___
+  /\  ._  ._  | |_  ._ _   _. _|_
+ /--\ |_) |_) | | | | (/_ (_|  |_
+      |   |
 """
 
 
@@ -40,28 +38,28 @@ def build_args():
     Constructs command line arguments for the vulnerability-db tool
     """
     parser = argparse.ArgumentParser(
-        description="AppThreat's vulnerability database and package search library with a built-in file based storage"
+        description="AppThreat's vulnerability database and package search library with a built-in sqlite based storage."
     )
     parser.add_argument(
         "--clean",
         action="store_true",
         default=False,
         dest="clean",
-        help="Clear the vulnerability database cache from platform specific user_data_dir",
+        help="Clear the vulnerability database cache from platform specific user_data_dir.",
     )
     parser.add_argument(
         "--cache",
         action="store_true",
         default=False,
         dest="cache",
-        help="Cache vulnerability information in platform specific user_data_dir",
+        help="Cache vulnerability information in platform specific user_data_dir.",
     )
     parser.add_argument(
         "--cache-os",
         action="store_true",
         default=False,
         dest="cache_os",
-        help="Cache OS vulnerability information in platform specific user_data_dir",
+        help="Cache OS vulnerability information in platform specific user_data_dir.",
     )
     parser.add_argument(
         "--only-osv",
@@ -78,85 +76,34 @@ def build_args():
         help="Use only Aqua vuln-list as the source. Use with --cache.",
     )
     parser.add_argument(
-        "--sync",
-        action="store_true",
-        default=False,
-        dest="sync",
-        help="Sync to receive the latest vulnerability data. Should have invoked cache first.",
-    )
-    parser.add_argument(
-        "--sync-npm",
-        action="store_true",
-        default=False,
-        dest="sync_npm",
-        help="Sync from npm.",
-    )
-    parser.add_argument(
-        "--sync-github",
+        "--only-ghsa",
         action="store_true",
         default=False,
-        dest="sync_github",
-        help="Sync from github.",
+        dest="only_ghsa",
+        help="Use only recent ghsa as the source. Use with --cache.",
     )
     parser.add_argument(
         "--search",
         dest="search",
-        help="Search for package and version in the database. Use colon to separate package and version. Use comma to specify multiple values. Eg: android:8.0",
-    )
-    parser.add_argument(
-        "--search-npm",
-        dest="search_npm",
-        help="Search for package and version in the database. Use colon to separate package and version. Use comma to specify multiple values. Eg: android:8.0",
-    )
-    parser.add_argument(
-        "--list",
-        action="store_true",
-        default=False,
-        dest="list",
-        help="List data in the db as a table",
+        help="Search for the package or CVE ID in the database. Use purl, cpe, or colon-separated values.",
     )
     return parser.parse_args()
 
 
 def print_results(results):
-    """Pretty print report summary"""
-    table = []
-    added_list = []
-    headers = [
-        "Id",
-        "Package",
-        "Affected Version",
-        "Fix Version",
-        "CWE",
-        "Severity",
-        "Score",
-        "Description",
-    ]
+    table = Table(title="VDB Results")
+    table.add_column("CVE", justify="left")
+    table.add_column("Type")
+    table.add_column("Namespace")
+    table.add_column("Name")
+    table.add_column("Source Data")
     for res in results:
-        vuln_occ_dict = res.to_dict()
-        vid = vuln_occ_dict.get("id")
-        package_type = vuln_occ_dict.get("type")
-        if vid not in added_list:
-            package_issue = res.package_issue
-            full_pkg = package_issue.affected_location.package
-            if package_issue.affected_location.vendor:
-                full_pkg = f"{package_issue.affected_location.vendor}:{package_issue.affected_location.package}"
-            if package_type and package_type not in ("*", "o", "h"):
-                full_pkg = package_type + ":" + full_pkg
-            table.append(
-                [
-                    vid,
-                    full_pkg,
-                    package_issue.affected_location.version,
-                    package_issue.fixed_location,
-                    vuln_occ_dict.get("problem_type"),
-                    vuln_occ_dict.get("severity"),
-                    vuln_occ_dict.get("cvss_score"),
-                    vuln_occ_dict.get("short_description"),
-                ]
-            )
-            added_list.append(vid)
-    print(tabulate(table, headers, tablefmt="grid"))
+        table.add_row(res.get("cve_id"), res.get("type"),
+                      res.get("namespace", ""), res.get("name"),
+                      Syntax(orjson.dumps(
+                          res.get("source_data").model_dump(mode="json", exclude_none=True),
+                          option=orjson.OPT_INDENT_2 | orjson.OPT_APPEND_NEWLINE).decode("utf-8", errors="ignore"), "json", word_wrap=True))
+    console.print(table)
 
 
 def main():
@@ -167,10 +114,14 @@ def main():
         if os.path.exists(config.DATA_DIR):
             shutil.rmtree(config.DATA_DIR, ignore_errors=True)
     if args.cache or args.cache_os:
+        db_lib.get()
+        db_lib.clear_all()
         if args.only_osv:
             sources = [OSVSource()]
         elif args.only_aqua:
             sources = [AquaSource()]
+        elif args.only_ghsa:
+            sources = [GitHubSource()]
         else:
             sources = [OSVSource(), GitHubSource()]
         # AquaSource also includes NVD
@@ -179,49 +130,20 @@ def main():
         for s in sources:
             LOG.info("Refreshing %s", s.__class__.__name__)
             s.refresh()
-    elif args.sync:
-        for s in (GitHubSource(),):
-            LOG.info("Syncing %s", s.__class__.__name__)
-            s.download_recent()
-    if args.sync_npm:
-        for s in (NpmSource(),):
-            LOG.info("Syncing %s", s.__class__.__name__)
-            s.download_recent()
-    if args.sync_github:
-        for s in (GitHubSource(),):
-            LOG.info("Syncing %s", s.__class__.__name__)
-            s.download_recent()
-    if args.search_npm:
-        source = NpmSource()
-        results = source.bulk_search(config.NPM_APP_INFO, [args.search_npm])
-        print_results(results)
-    if args.list:
-        db = db_lib.get()
-        results = db_lib.list_all_occurrence(db)
-        print_results(results)
-    elif args.search:
-        LOG.info("Vulnerability database loaded from %s", config.VDB_BIN_FILE)
-        db = db_lib.get()
+        cve_data_count, cve_index_count = db_lib.stats()
+        console.print("cve_data_count", cve_data_count, "cve_index_count", cve_index_count)
+        db_lib.optimize_and_close_all()
+    if args.search:
         if args.search.startswith("pkg:"):
-            index_hits = db_lib.bulk_index_search([{"purl": args.search}])
-            results = db_lib.pkg_bulk_search(db, index_hits)
+            results = search.search_by_purl_like(args.search, with_data=True)
+        elif args.search.startswith("CVE-") or args.search.startswith("GHSA-") or args.search.startswith("MAL-"):
+            results = search.search_by_cve(args.search, with_data=True)
+        else:
+            results = search.search_by_cpe_like(args.search, with_data=True)
+        if results:
             print_results(results)
         else:
-            search_list = re.split(r"[,|;]", args.search)
-            for pkg_info in search_list:
-                pstr = re.split(r"[:=@]", pkg_info)
-                if pstr:
-                    if len(pstr) == 2 and db_lib.index_search(*pstr):
-                        results = db_lib.pkg_search(db, *pstr)
-                        print_results(results)
-                    elif len(pstr) == 3:
-                        index_hits = db_lib.bulk_index_search(
-                            [{"vendor": pstr[0], "name": pstr[1], "version": pstr[2]}]
-                        )
-                        results = db_lib.pkg_bulk_search(db, index_hits)
-                        print_results(results)
-                    else:
-                        print("No vulnerability found!")
+            console.print("No results found!")
 
 
 if __name__ == "__main__":
diff --git a/vdb/lib/__init__.py b/vdb/lib/__init__.py
index 82a64e5..29b8d9d 100644
--- a/vdb/lib/__init__.py
+++ b/vdb/lib/__init__.py
@@ -8,8 +8,24 @@
 import orjson
 
 # Known application package types
+# See https://github.com/package-url/purl-spec/blob/master/PURL-TYPES.rst
 KNOWN_PKG_TYPES = [
+    "alpm",
+    "bitbucket",
+    "bitnami",
+    "cargo",
     "composer",
+    "cocoapods",
+    "conda",
+    "cpan",
+    "cran",
+    "docker",
+    "generic",
+    "github",
+    "huggingface",
+    "mlflow",
+    "qpkg",
+    "oci",
     "maven",
     "npm",
     "nuget",
@@ -34,6 +50,7 @@
     "linux",
     "swid",
     "oss-fuzz",
+    "ebuild"
 ]
 
 # Maps variations of string to package types
@@ -46,7 +63,7 @@
     "gem": ["ruby"],
     "rubygems": ["ruby", "gem"],
     "golang": ["go"],
-    "crates": ["rust", "crates.io", "cargo"],
+    "cargo": ["rust", "crates.io", "cargo"],
     "pub": ["dart"],
     "hex": ["elixir"],
     "github": ["actions"],
@@ -62,7 +79,8 @@
         "opensuse",
         "fedora",
     ],
-    "alpm": ["arch"],
+    "alpm": ["arch", "archlinux"],
+    "ebuild": ["gentoo", "portage"]
 }
 
 # CPE Regex
@@ -79,12 +97,12 @@
 class VulnerabilitySource(metaclass=ABCMeta):
     @classmethod
     @abstractmethod
-    def download_all(cls, local_store=True):
+    def download_all(cls):
         pass
 
     @classmethod
     @abstractmethod
-    def download_recent(cls, local_store=True):
+    def download_recent(cls):
         pass
 
     @classmethod
@@ -97,6 +115,11 @@ def bulk_search(cls, app_info, pkg_list):
     def convert(cls, data):
         pass
 
+    @classmethod
+    @abstractmethod
+    def convert5(cls, data):
+        pass
+
     @classmethod
     @abstractmethod
     def store(cls, data):
@@ -178,19 +201,19 @@ class CvssV3:
     vector_string: str
 
     def __init__(
-        self,
-        base_score,
-        exploitability_score,
-        impact_score,
-        attack_vector,
-        attack_complexity,
-        privileges_required,
-        user_interaction,
-        scope,
-        confidentiality_impact,
-        integrity_impact,
-        availability_impact,
-        vector_string=None,
+            self,
+            base_score,
+            exploitability_score,
+            impact_score,
+            attack_vector,
+            attack_complexity,
+            privileges_required,
+            user_interaction,
+            scope,
+            confidentiality_impact,
+            integrity_impact,
+            availability_impact,
+            vector_string=None,
     ):
         self.base_score = base_score
         self.exploitability_score = exploitability_score
@@ -229,13 +252,13 @@ class PackageIssue:
     """Package issue class"""
 
     def __init__(
-        self,
-        affected_location,
-        fixed_location,
-        mii=None,
-        mai=None,
-        mie=None,
-        mae=None,
+            self,
+            affected_location,
+            fixed_location,
+            mii=None,
+            mai=None,
+            mie=None,
+            mae=None,
     ):
         self.affected_location = VulnerabilityLocation.from_values(
             affected_location,
@@ -286,19 +309,19 @@ class VulnerabilityDetail:
     """Vulnerability detail class"""
 
     def __init__(
-        self,
-        cpe_uri: str,
-        package: str,
-        min_affected_version_including: str | None,
-        max_affected_version_including: str | None,
-        min_affected_version_excluding: str | None,
-        max_affected_version_excluding: str | None,
-        severity: str,
-        description: str | None,
-        fixed_location: str,
-        package_type: str,
-        is_obsolete: str,
-        source_update_time: str,
+            self,
+            cpe_uri: str,
+            package: str,
+            min_affected_version_including: str | None,
+            max_affected_version_including: str | None,
+            min_affected_version_excluding: str | None,
+            max_affected_version_excluding: str | None,
+            severity: str,
+            description: str | None,
+            fixed_location: str,
+            package_type: str,
+            is_obsolete: str,
+            source_update_time: str,
     ):
         parts = CPE_REGEX.match(cpe_uri)
         self.cpe_uri = cpe_uri
@@ -362,11 +385,11 @@ def get_type(cpe_uri, package_type):
             if ptype in KNOWN_PKG_TYPES:
                 return ptype
             if all_parts and (
-                (all_parts.group("target_sw") and all_parts.group("target_sw") != "*")
-                or (
-                    all_parts.group("sw_edition")
-                    and all_parts.group("sw_edition") != "*"
-                )
+                    all_parts.group("target_sw") and all_parts.group("target_sw") != "*"
+                    or (
+                        all_parts.group("sw_edition")
+                        and all_parts.group("sw_edition") != "*"
+                    )
             ):
                 for vk, vv in PKG_TYPES_MAP.items():
                     target_sw = all_parts.group("target_sw")
@@ -425,18 +448,18 @@ class Vulnerability:
     cvss_v3: CvssV3
 
     def __init__(
-        self,
-        vid: str,
-        assigner: str,
-        problem_type: str,
-        score: float,
-        severity: str,
-        description: str,
-        related_urls: list[str],
-        details: list[VulnerabilityDetail],
-        cvss_v3: CvssV3,
-        source_update_time: str,
-        source_orig_time: str,
+            self,
+            vid: str,
+            assigner: str,
+            problem_type: str,
+            score: float,
+            severity: str,
+            description: str,
+            related_urls: list[str],
+            details: list[VulnerabilityDetail],
+            cvss_v3: CvssV3,
+            source_update_time: str,
+            source_orig_time: str,
     ):
         self.id = vid
         self.assigner = assigner
@@ -485,11 +508,11 @@ def __init__(self, cpe_uri, vendor, package, version):
 
     @staticmethod
     def from_values(
-        cpe_uri,
-        mii=None,
-        mai=None,
-        mie=None,
-        mae=None,
+            cpe_uri,
+            mii=None,
+            mai=None,
+            mie=None,
+            mae=None,
     ):
         parts = None
         version = "*"
@@ -565,21 +588,21 @@ class VulnerabilityOccurrence:
     matched_by: str
 
     def __init__(
-        self,
-        oid,
-        problem_type,
-        otype,
-        severity,
-        cvss_score,
-        cvss_v3,
-        package_issue,
-        short_description,
-        long_description,
-        related_urls,
-        effective_severity,
-        source_update_time,
-        source_orig_time,
-        matched_by,
+            self,
+            oid,
+            problem_type,
+            otype,
+            severity,
+            cvss_score,
+            cvss_v3,
+            package_issue,
+            short_description,
+            long_description,
+            related_urls,
+            effective_severity,
+            source_update_time,
+            source_orig_time,
+            matched_by,
     ):
         self.id = oid
         self.problem_type = problem_type
diff --git a/vdb/lib/aqua.py b/vdb/lib/aqua.py
index e949a1e..3235673 100644
--- a/vdb/lib/aqua.py
+++ b/vdb/lib/aqua.py
@@ -23,29 +23,10 @@
 DOWNLOAD_CHUNK_SIZE = 4096
 
 
-threat_to_severity = {
-    "unspecified": "LOW",
-    "": "LOW",
-    "negligible": "LOW",
-    "low": "LOW",
-    "unimportant": "LOW",
-    "severity_low": "LOW",
-    "medium": "MEDIUM",
-    "severity_medium": "MEDIUM",
-    "moderate": "MEDIUM",
-    "severity_moderate": "MEDIUM",
-    "important": "HIGH",
-    "high": "HIGH",
-    "severity_important": "HIGH",
-    "critical": "CRITICAL",
-    "severity_critical": "CRITICAL",
-}
-
-
 class AquaSource(NvdSource):
     """Aqua CVE source"""
 
-    def download_all(self, local_store=True):
+    def download_all(self):
         """Download all cve data"""
         # For performance do not retain the whole data in-memory
         # See: https://github.com/AppThreat/vulnerability-db/issues/27
@@ -53,7 +34,7 @@ def download_all(self, local_store=True):
         self.fetch(config.VULN_LIST_URL)
         return data_list
 
-    def download_recent(self, local_store=True):
+    def download_recent(self):
         pass
 
     def _process_zip(self, zname):
@@ -65,7 +46,7 @@ def _process_zip(self, zname):
                         try:
                             json_data = orjson.loads(cve_data)
                             ret_data = self.convert(json_data)
-                            self.store(ret_data, reindex=False)
+                            self.store(ret_data)
                         except Exception:
                             pass
 
@@ -113,16 +94,16 @@ def convert(self, cve_data):
     @staticmethod
     def is_supported_source(zfname):
         for distro in (
-            "alpine",
-            "cwe",
-            "ghsa",
-            "go",
-            "osv",
-            "redhat-cpe",
-            "kevc",
-            "oval",
-            "glad",
-            "mariner",
+                "alpine",
+                "cwe",
+                "ghsa",
+                "go",
+                "osv",
+                "redhat-cpe",
+                "kevc",
+                "oval",
+                "glad",
+                "mariner",
         ):
             if distro in zfname:
                 return False
@@ -168,11 +149,12 @@ def alsa_to_vuln(cve_data):
             )
 
         assigner = cve_data.get("fromstr", "")
-        severity = threat_to_severity[cve_data.get("severity").lower()]
+        severity = config.THREAT_TO_SEVERITY[cve_data.get("severity").lower()]
         score, severity, vector_string, attack_complexity = get_default_cve_data(
             severity
         )
         exploitability_score = score
+        user_interaction = "REQUIRED"
         vendor = "almalinux"
         pkg_name = packages[0].get("name")
         version_start_including = ""
@@ -222,6 +204,7 @@ def alsa_to_vuln(cve_data):
                 severity=severity,
                 attackComplexity=attack_complexity,
                 score=score,
+                userInteraction=user_interaction,
                 exploitabilityScore=exploitability_score,
                 publishedDate=published_date,
                 lastModifiedDate=last_modified_date,
@@ -254,11 +237,12 @@ def alas_rlsa_to_vuln(cve_data, vendor):
             cve_id = cve_data.get("cveids")[0]
         description = cve_data.get("description", "")
         assigner = vendor
-        severity = threat_to_severity[cve_data.get("severity").lower()]
+        severity = config.THREAT_TO_SEVERITY[cve_data.get("severity").lower()]
         score, severity, vector_string, attack_complexity = get_default_cve_data(
             severity
         )
         exploitability_score = score
+        user_interaction = "REQUIRED"
         published_date = cve_data.get("issued", {}).get("date")
         last_modified_date = cve_data.get("updated", {}).get("date")
         done_pkgs = {}
@@ -303,6 +287,7 @@ def alas_rlsa_to_vuln(cve_data, vendor):
                     severity=severity,
                     attackComplexity=attack_complexity,
                     score=score,
+                    userInteraction=user_interaction,
                     exploitabilityScore=exploitability_score,
                     publishedDate=published_date,
                     lastModifiedDate=last_modified_date,
@@ -337,13 +322,14 @@ def ubuntu_to_vuln(cve_data):
             return ret_data
         if not description and cve_data.get("UbuntuDescription"):
             description = cve_data.get("UbuntuDescription")
-        assigner = "ubuntu"
+        assigner = "canonical"
         vendor = "ubuntu"
-        severity = threat_to_severity[cve_data.get("Priority").lower()]
+        severity = config.THREAT_TO_SEVERITY[cve_data.get("Priority").lower()]
         score, severity, vector_string, attack_complexity = get_default_cve_data(
             severity
         )
         exploitability_score = score
+        user_interaction = "REQUIRED"
         published_date = cve_data.get("PublicDate")
         last_modified_date = cve_data.get("PublicDate")
         for pkg_name, distro_obj in packages.items():
@@ -352,8 +338,8 @@ def ubuntu_to_vuln(cve_data):
                 # DNE - does not exist
                 # needs-triage - Vulnerability is not confirmed.
                 if status_obj.get("Status") in (
-                    "DNE",
-                    "needs-triage",
+                        "DNE",
+                        "needs-triage",
                 ):
                     continue
                 version_start_including = ""
@@ -368,30 +354,15 @@ def ubuntu_to_vuln(cve_data):
                 if ":" in fix_note:
                     fix_note = fix_note.split(":")[-1]
                 # Released CVEs have fixes
-                if (
-                    status_obj.get("Status")
-                    in (
-                        "not-affected",
-                        "released",
-                    )
-                    and " " not in fix_note
-                    and "CVE" not in fix_note
-                ):
+                if status_obj.get("Status") in ("not-affected", "released") and " " not in fix_note and "CVE" not in fix_note:
                     fix_version_start_including = fix_note
                     version_end_excluding = fix_note
                 # Handle CVEs that are deferred
                 # Let's include the vulnerabilities that did not get a fix
-                if (
-                    status_obj.get("Status")
-                    in (
-                        "deferred",
-                        "needed",
-                    )
-                    and " " not in fix_note
-                ):
-                    version_end_including = config.placeholder_fix_version
+                if status_obj.get("Status") in ("deferred", "needed") and " " not in fix_note:
+                    version_end_including = config.PLACEHOLDER_FIX_VERSION
                 if status_obj.get("Status") in ("ignored",):
-                    version_end_including = config.placeholder_fix_version
+                    version_end_including = config.PLACEHOLDER_FIX_VERSION
                 if pkg_name and (fix_version_start_including or version_end_including):
                     for full_pkg_name in (f"{distro_name}/{pkg_name}",):
                         tdata = config.CVE_TPL % dict(
@@ -416,6 +387,7 @@ def ubuntu_to_vuln(cve_data):
                             severity=severity,
                             attackComplexity=attack_complexity,
                             score=score,
+                            userInteraction=user_interaction,
                             exploitabilityScore=exploitability_score,
                             publishedDate=published_date,
                             lastModifiedDate=last_modified_date,
@@ -457,14 +429,16 @@ def redhat_to_vuln(cve_data):
                 score = float(score)
             except Exception:
                 pass
-        severity = threat_to_severity[cve_data.get("threat_severity").lower()]
+        severity = config.THREAT_TO_SEVERITY[cve_data.get("threat_severity").lower()]
         cvss3_obj = get_cvss3_from_vector(vector_string)
         exploitability_score = ""
         attack_complexity = ""
+        user_interaction = "REQUIRED"
         if cvss3_obj:
             exploitability_score = cvss3_obj.get("temporalScore")
             attack_complexity = cvss3_obj.get("attackComplexity")
             score = cvss3_obj.get("baseScore")
+            user_interaction = cvss3_obj.get("userInteraction")
         published_date = cve_data.get("public_date")
         last_modified_date = cve_data.get("public_date")
         done_pkgs = {}
@@ -524,6 +498,7 @@ def redhat_to_vuln(cve_data):
                     severity=severity,
                     attackComplexity=attack_complexity,
                     score=score,
+                    userInteraction=user_interaction,
                     exploitabilityScore=exploitability_score,
                     publishedDate=published_date,
                     lastModifiedDate=last_modified_date,
@@ -551,7 +526,7 @@ def arch_to_vuln(cve_data):
             cve_id = cve_data.get("issues")[0]
         description = cve_data.get("type", "")
         assigner = "archlinux"
-        severity = threat_to_severity[cve_data.get("severity").lower()]
+        severity = config.THREAT_TO_SEVERITY[cve_data.get("severity").lower()]
         score, severity, vector_string, attack_complexity = get_default_cve_data(
             severity
         )
@@ -589,6 +564,7 @@ def arch_to_vuln(cve_data):
                     severity=severity,
                     attackComplexity=attack_complexity,
                     score=score,
+                    userInteraction="REQUIRED",
                     exploitabilityScore=score,
                     publishedDate=published_date,
                     lastModifiedDate=last_modified_date,
@@ -629,7 +605,7 @@ def suse_to_vuln(self, cve_data):
                 description = avuln.get("Description")
                 threats = avuln.get("Threats")
                 if threats and len(threats) > 0:
-                    severity = threat_to_severity[threats[0].get("Severity").lower()]
+                    severity = config.THREAT_TO_SEVERITY[threats[0].get("Severity").lower()]
                 cwe_id = ""
                 cve_references = avuln.get("References", [])
                 references = []
@@ -684,6 +660,7 @@ def suse_to_vuln(self, cve_data):
                             severity=severity,
                             attackComplexity=attack_complexity,
                             score=score,
+                            userInteraction="REQUIRED",
                             exploitabilityScore=score,
                             publishedDate=published_date,
                             lastModifiedDate=last_modified_date,
@@ -710,7 +687,7 @@ def photon_to_vuln(cve_data):
         """.format(
             cve_data.get("aff_ver")
         )
-        assigner = "photon"
+        assigner = "vmware"
         score = cve_data.get("cve_score")
         severity = convert_score_severity(score)
         newscore, severity, vector_string, attack_complexity = get_default_cve_data(
@@ -755,6 +732,7 @@ def photon_to_vuln(cve_data):
                 severity=severity,
                 attackComplexity=attack_complexity,
                 score=score,
+                userInteraction="REQUIRED",
                 exploitabilityScore=score,
                 publishedDate=published_date,
                 lastModifiedDate=last_modified_date,
@@ -788,17 +766,16 @@ def debian_to_vuln(cve_data):
         filtered_annotations = [
             ann
             for ann in annotations
-            if ann.get("Release")
-            and (ann.get("Version") or ann.get("Kind") == "not-affected")
+            if ann.get("Release") and (ann.get("Version") or ann.get("Kind") == "not-affected")
         ]
         for ann in annotations:
             if ann.get("Type") == "RESERVED" or ann.get("Original") == "RESERVED":
                 continue
             # Try to dealias
             if (
-                not cve_id.startswith("CVE")
-                and ann.get("Type") == "xref"
-                and ann.get("Bugs")
+                    not cve_id.startswith("CVE")
+                    and ann.get("Type") == "xref"
+                    and ann.get("Bugs")
             ):
                 aliases_block = """
 ## Related CVE(s)
@@ -813,13 +790,13 @@ def debian_to_vuln(cve_data):
                         break
             kind = ann.get("Kind")
             if kind not in (
-                "fixed",
-                "unfixed",
-                "no-dsa",
-                "end-of-life",
-                "ignored",
-                "not-affected",
-                "postponed",
+                    "fixed",
+                    "unfixed",
+                    "no-dsa",
+                    "end-of-life",
+                    "ignored",
+                    "not-affected",
+                    "postponed",
             ):
                 continue
             pkg_name = ann.get("Package")
@@ -831,22 +808,22 @@ def debian_to_vuln(cve_data):
                 continue
             if distro_name:
                 pkg_name = f"{distro_name}/{pkg_name}"
-            version = ann.get("Version", config.placeholder_fix_version)
+            version = ann.get("Version", config.PLACEHOLDER_FIX_VERSION)
             # We need to track not-affected entries with a special exclude version
             if kind == "not-affected":
-                version = config.placeholder_exclude_version
+                version = config.PLACEHOLDER_EXCLUDE_VERSION
             # Remove epoch
             if ":" in version:
                 version = version.split(":")[-1]
             severity = "MEDIUM"
             # Try harder to set LOW priority
             if kind == "postponed" or (
-                ann.get("Description")
-                and "minor issue" in ann.get("Description").lower()
+                    ann.get("Description")
+                    and "minor issue" in ann.get("Description").lower()
             ):
                 severity = "LOW"
             if ann.get("Severity"):
-                severity = threat_to_severity.get(ann.get("Severity"), "MEDIUM")
+                severity = config.THREAT_TO_SEVERITY.get(ann.get("Severity"), "MEDIUM")
             score, severity, vector_string, attack_complexity = get_default_cve_data(
                 severity
             )
@@ -883,6 +860,7 @@ def debian_to_vuln(cve_data):
                     severity=severity,
                     attackComplexity=attack_complexity,
                     score=score,
+                    userInteraction="REQUIRED",
                     exploitabilityScore=score,
                     publishedDate=published_date,
                     lastModifiedDate=last_modified_date,
@@ -941,6 +919,7 @@ def wolfi_to_vuln(cve_data):
                     severity=severity,
                     attackComplexity=attack_complexity,
                     score=score,
+                    userInteraction="REQUIRED",
                     exploitabilityScore=score,
                     publishedDate=published_date,
                     lastModifiedDate=last_modified_date,
diff --git a/vdb/lib/cna.py b/vdb/lib/cna.py
new file mode 100644
index 0000000..afe6e2f
--- /dev/null
+++ b/vdb/lib/cna.py
@@ -0,0 +1,350 @@
+"""
+Official CNA short name to org id mapping. This is created using the below code snippet from the latest CVE v5 list
+
+```shell
+git clone https://github.com/CVEProject/cvelistV5
+cd cvelistV5
+```
+
+```python
+from pathlib import Path
+import json
+
+assigner_map = {}
+for f in Path(".").glob('**/*/CVE*.json'):
+    print (f)
+    with open(f, mode="r") as fp:
+        obj = json.load(fp)
+        if obj.get("cveMetadata", {}).get("assignerShortName"):
+            assigner_map[obj.get("cveMetadata", {}).get("assignerShortName").lower()] = obj.get("cveMetadata", {}).get("assignerOrgId")
+
+with open("maps.json", mode="w") as fp:
+    json.dump(assigner_map, fp, indent=4)
+```
+"""
+ASSIGNER_UUID_MAP = {
+    "nvd@nist.gov": "8254265b-2729-46b6-b9e3-3dfca2d5bfca",
+    "cve@mitre.org": "8254265b-2729-46b6-b9e3-3dfca2d5bfca",
+    "mitre": "8254265b-2729-46b6-b9e3-3dfca2d5bfca",
+    "redhat": "53f830b8-0a3f-465b-8143-3b8a9948e749",
+    "certcc": "37e5125f-f79b-445b-8fad-9564f167944b",
+    "sgi": "bc94ec7e-8909-4cbb-83df-d2fc9330fa88",
+    "apple": "286789f9-fbc2-4510-9f9a-43facdede74c",
+    "debian": "79363d38-fa19-49d1-9214-5f28da3f3ac5",
+    "adobe": "078d4453-3bcd-4900-85e6-15281da43538",
+    "ibm": "9a959283-ebb5-44b6-b705-dcc2bbced522",
+    "freebsd": "63664ac6-956c-4cba-a5d0-f46076e16109",
+    "canonical": "cc1ad9ee-3454-478d-9317-d3e869d708bc",
+    "microsoft": "f38d906d-7342-40ea-92c1-6c4a2c6478c8",
+    "vuldb": "1af790b2-7ee1-4545-860a-a788eba489b5",
+    "microfocus": "f81092c5-7f14-476d-80dc-24857f90be84",
+    "symantec": "80d3bcb6-88de-48c2-a47e-aebf795f19b5",
+    "apache": "f0158376-9dc2-43b6-827c-5f631a4d8d09",
+    "flexera": "44d08088-2bea-4760-83a6-1e9be26b15ab",
+    "cisco": "d1c1063e-7a18-46af-9102-31f8928bc633",
+    "oracle": "43595867-4340-4103-b7a2-9a5208d29a85",
+    "hp": "74586083-13ce-40fd-b46a-8e5d23cfbcb2",
+    "jpcert": "ede6fdc4-6654-4307-a26d-3331c018e2ce",
+    "dell": "c550e75a-17ff-4988-97f0-544cde3820fe",
+    "mozilla": "f16b083a-5664-49f3-a51e-8d479e5ed7fe",
+    "airbus": "24a3c815-5f22-4d74-967a-30958d6466f4",
+    "icscert": "7d14cffa-0d7d-4270-9dc0-52cabd5a23a6",
+    "chrome": "ebfee0ef-53dd-4cf3-9e2a-08a5bd7a7e28",
+    "isc": "404fd4d2-a609-4245-b543-2c944a302a22",
+    "vmware": "dcf2e128-44bd-42ed-91e8-88f912c1401d",
+    "go": "1bb62c36-49e3-4200-9d77-64a1400537cc",
+    "larry_cashdollar": "461b2335-328f-427d-ae3d-eff7d6814455",
+    "google_android": "baff130e-b8d5-4e15-b3d3-c3cf5d5545c6",
+    "intel": "6dda929c-bb53-4a77-a76d-48e79601a1ce",
+    "qualcomm": "2cfc7d3e-20d3-47ac-8db7-1b7285aff15f",
+    "hackerone": "36234546-b8fa-4601-9d6f-f4e334aa8ea1",
+    "fortinet": "6abe59d8-c742-4dff-8ce8-9b0ca1073da8",
+    "siemens": "cec7a2ec-15b4-4faf-bd53-b40f371f3a77",
+    "huawei": "25ac1063-e409-4190-8079-24548c77ea2e",
+    "wpscan": "1bfdd5d7-9bf6-4a53-96ea-42e2716d7a81",
+    "certvde": "270ccfa6-a436-4e77-922e-914ec3a9685c",
+    "wordfence": "b15e7b5b-3da4-40ae-a43c-f7aa60e62599",
+    "zdi": "99f1926a-a320-47d8-bbb5-42feb611262e",
+    "ca": "e291eae9-7c0a-46ac-ba7d-5251811f8b7f",
+    "hpe": "eb103674-0d28-4225-80f8-39fb86215de0",
+    "juniper": "8cbe9d5a-a066-4c94-8978-4b15efeae968",
+    "tibco": "4f830c72-39e4-45f6-a99f-78cc01ae04db",
+    "elastic": "271b6943-45a9-4f3a-ab4e-976f3fa05b5a",
+    "synology": "db201096-a0cc-46c7-9a55-61d9e221bf01",
+    "blackberry": "dbe78b00-5e7b-4fda-8748-329789ecfc5c",
+    "nvidia": "9576f279-3576-44b5-a4af-b9a8644b2de6",
+    "f5": "9dacffd4-cb11-413f-8451-fbbfd4ddc0ab",
+    "puppet": "ca2a266c-be2f-4d4b-92d0-47b76b1a9c4e",
+    "rapid7": "9974b330-7714-4307-a722-5648477acda7",
+    "netapp": "11fdca00-0482-4c88-a206-37f9c182c87d",
+    "openssl": "3a12439a-ef3a-4c79-92e6-6081a721f1e5",
+    "checkpoint": "897c38be-0345-43cd-b6cf-fe179e0c4f45",
+    "trellix": "01626437-bf8f-4d1c-912a-893b5eb04808",
+    "brocade": "87b297d7-335e-4844-9551-11b97995a791",
+    "lenovo": "da227ddf-6e25-4b41-b023-0f976dcaca4b",
+    "talos": "b86d76f8-0f8a-4a96-a78d-d8abfc7fc29b",
+    "yandex": "a51c9250-e584-488d-808b-03e6f1386796",
+    "obdev": "e0cebea7-a708-4bfe-81c1-855d35eb4544",
+    "marklogic": "e0dcfa7a-8a53-4e5c-958e-bbea8ff83f37",
+    "krcert": "cdd7a122-0fae-4202-8d86-14efbacc2863",
+    "joshbressers": "46fe6300-5254-4a98-9594-a9567bec8179",
+    "kubernetes": "a6081bf6-c852-4425-ad4f-a67919267565",
+    "fedora": "92fb86c3-55a5-4fb5-9c3f-4757b9e96dc5",
+    "dwf": "7556d962-6fb7-411e-85fa-6cd62f095ba8",
+    "netflix": "ea63fd80-a441-4c7b-ba78-e48a8071cae2",
+    "zte": "6786b568-6808-4982-b61f-398b0d9679eb",
+    "trendmicro": "7f7bd7df-cffe-4fdb-ab6d-859363b89272",
+    "duo": "7cd4c57f-0a88-4dda-be53-70336b413766",
+    "tenable": "5ac1ecc2-367a-4d16-a0b2-35d495ddd0be",
+    "kaspersky": "e45d732a-8f6b-4b6b-be76-7420f6a2b988",
+    "qnap": "2fd009eb-170a-4625-932b-17a53af1051f",
+    "zephyr": "e2e69745-5e70-4e92-8431-deb5529a81ad",
+    "atlassian": "f08a6ab8-ed46-4c22-8884-d911ccfe3c66",
+    "forcepoint": "e23ea22c-8c39-4eff-8980-2881e5ae54e2",
+    "nodejs": "386269d4-a6c6-4eaa-bf8e-bc0b0d010558",
+    "sap": "e4686d1a-f260-4930-ac4c-2f5c992778dd",
+    "hcl": "1e47fe04-f25f-42fa-b674-36de2c5e3cfc",
+    "vaadin": "9e0f3122-90e9-42d5-93de-8c6b98deef7e",
+    "incibe": "0cbda920-cd7f-484a-8e76-bf7f4b7f4516",
+    "axis": "f2daf9a0-02c2-4b83-a01d-63b3b304b807",
+    "drupal": "2c85b837-eb8b-40ed-9d74-228c62987387",
+    "netgear": "a2826606-91e7-4eb6-899e-8484bd4575d5",
+    "eclipse": "e51fbebd-6053-4e49-959f-1b94eeb69a2c",
+    "schneider": "076d1eb6-cfab-4401-b34d-6dfc2a413bdb",
+    "dahua": "79ee569e-7d1e-4364-98f0-3a18e2a739ad",
+    "snyk": "bae035ff-b466-4ff4-94d0-fc9efd9e1730",
+    "palo_alto": "d6c1279f-00f6-4ef7-9217-f89ffe703ec0",
+    "hillstone": "2b565742-f273-46f9-b583-07c1fcdea31a",
+    "naver": "f9629fae-ca2e-4fbf-9785-3ed86476aef6",
+    "avaya": "9d670455-bdb5-4cca-a883-5914865f5d96",
+    "odoo": "22c90092-d340-4fb8-a06e-f1193e012523",
+    "tcpdump": "cfdbb673-b408-4d03-89c1-c3d73ed80896",
+    "cspcert": "8207d157-a9c5-4c55-ab16-b67a7ac0b646",
+    "twcert": "cded6c7f-6ce5-4948-8f87-aa7a3bbb6b0e",
+    "360st": "40f8fa2f-7875-43d0-a30e-e901a5537754",
+    "facebook": "4fc57720-52fe-4431-a0fb-3d2c8747b827",
+    "mongodb": "a39b4221-9bd0-4244-95fc-f3e2e07f1deb",
+    "hikvision": "da451dce-859b-4e51-8b87-9c8b60d19b32",
+    "sonicwall": "44b2ff79-1416-4492-88bb-ed0da00c7315",
+    "jenkins": "39769cd5-e6e2-4dc8-927e-97b3aa056f5b",
+    "php": "dd77f84a-d19a-4638-8c3d-a322d820ed2b",
+    "pivotal": "862b2186-222f-48b9-af87-f1fb7bb26d03",
+    "bosch": "c95f66b2-7e7c-41c5-8f09-6f86ec68659c",
+    "bitdefender": "b3d5ebe7-963e-41fb-98e1-2edaeabb8f82",
+    "floragunn": "9f311a02-c44f-4938-8530-9219246b8255",
+    "salesforce": "c9b25dee-ae6d-4083-ba23-638c500cc364",
+    "github_m": "a0819718-46f1-4df5-94e2-005712e83aaa",
+    "suse": "404e59f5-483d-4b8a-8e7a-e67604dd8afb",
+    "abb": "2b718523-d88f-4f37-9bbd-300c20644bf9",
+    "opera": "9aee7086-24f5-48b4-9428-908ac90b8b54",
+    "linux": "416baaa9-dc9f-4396-8d5f-8c081fb06d67",
+    "autodesk": "7e40ea87-bc65-4944-9723-dd79dd760601",
+    "jci": "7281d04a-a537-43df-bfb4-fa4110af9d01",
+    "document fdn.": "4fe7d05b-1353-44cc-8b7a-1e416936dff2",
+    "alias": "dc524f69-879d-41dc-ab8f-724e78658a1a",
+    "github_p": "82327ea3-741d-41e4-88f8-2cf9e791e760",
+    "zscaler": "73c6f63b-efac-410d-a0a9-569700f85a04",
+    "oppo": "7f2b1ad8-5432-4d64-91a1-9099af1cc695",
+    "opentext": "f81092c5-7f14-476d-80dc-24857f90be84",
+    "silver peak": "83cc1b1a-46b0-4ac1-94f2-bbef3319bc4c",
+    "vivo": "c6f5cd8e-fe3d-4460-82c2-f8a4e7b272c8",
+    "amd": "b58fc414-a1e4-4f92-9d70-1add41838648",
+    "teradici": "ba3c294d-a544-4fff-ad44-2de7c7bbb6be",
+    "gitlab": "ceab7361-8a18-47b1-92ba-4d7d25f6715a",
+    "tigera": "e6d453f4-3dae-4941-bcea-9af25f4e824d",
+    "swi": "747bec18-acd0-4d99-a5c8-5e366c66ab7e",
+    "xiaomi": "b57733aa-7326-4f07-8e09-0be8e0df1909",
+    "openvpn": "36a55730-e66d-4d39-8ca6-3c3b3017965e",
+    "openeuler": "7e1ac599-2767-43fa-b3ea-f10178cc98f2",
+    "gallagher": "0c426f27-3ee1-4eff-be88-288d5a1822bc",
+    "otrs": "2e1bf29f-dc29-4ed8-830c-7b9348b6f0e8",
+    "cloudflare": "a22f1246-ba21-4bb4-a601-ad51614c1513",
+    "crafter": "4ff2b028-869f-4b00-a7b2-05997f6f14fd",
+    "vdoo": "6b4ace4a-d6e0-415b-9ce8-aa20e97e4b24",
+    "ea": "442d7a9f-d735-4edc-91a9-bdd618cbff51",
+    "mend": "478c68dd-22c1-4a41-97cd-654224dfacff",
+    "nlnet labs": "206fc3a0-e175-490b-9eaa-a5738056c9f6",
+    "secomea": "f2815942-3388-4c08-ba09-6c15850fda90",
+    "csw": "ee1bbb37-1770-46bd-bba8-910037954ee0",
+    "mattermost": "9302f53e-dde5-4bf3-b2f2-a83f91ac0eee",
+    "sick ag": "a6863dd2-93fc-443d-bef1-79f0b5020988",
+    "hitachi": "50d0f415-c707-4733-9afc-8f6c0e9b3f82",
+    "joomla": "6ff30186-7fb7-4ad9-be33-533e7b05e586",
+    "sophos": "526a354d-e866-4174-ae7d-bac848e5c4c5",
+    "eaton": "63703b7d-23e2-41ef-94b3-a3c6333f7759",
+    "google": "14ed7db2-1595-443d-9d34-6215bf890778",
+    "mitsubishi": "e0f77b61-78fd-4786-b3fb-1ee347a748ad",
+    "nec": "f2760a35-e0d8-4637-ac4c-cc1a2de3e282",
+    "patchstack": "21595511-bba5-4825-b968-b78d1f9984a3",
+    "mirantis": "ac17a704-eccd-4263-a802-5cee95c1d547",
+    "jfrog": "48a46f29-ae42-4e1d-90dd-c1676c1e5e6d",
+    "samsung mobile": "3af57064-a867-422c-b2ad-40307b65c458",
+    "splunk": "42b59230-ec95-491e-8425-5a5befa1a469",
+    "octopus": "6f4f8c89-ef06-4bae-a2a5-6734ddf76272",
+    "nozomi": "bec8025f-a851-46e5-b3a3-058e6b0aa23c",
+    "pega": "c91e5604-2bd1-401f-a0ec-b25342b57ef9",
+    "mautic": "4e531c38-7a33-45d3-98dd-d909c0d8852e",
+    "tianocore": "65518388-201a-4f93-8712-366d21fe8d2c",
+    "arista": "c8b34d1a-69ae-45c3-88fe-f3b3d44f39b7",
+    "xen": "23aa2041-22e1-471f-9209-9b7396fa234f",
+    "esri": "cedc17bb-4939-4f40-a1f4-30ae8af1094e",
+    "deepsurface": "17539d52-06bd-4776-9c7a-95dc115e9083",
+    "mediatek": "ee979b05-11f8-4f25-a7e0-a1fa9c190374",
+    "ping identity": "5998a2e9-ae88-42cd-b6e0-7564fd979f9e",
+    "solarwinds": "49f11609-934d-4621-84e6-e02e032104d6",
+    "@huntrdev": "c09c270a-b464-47c1-9133-acb35b22c19a",
+    "snps": "8cad7728-009c-4a3d-a95e-ca62e6ff8a0b",
+    "f-secureus": "126858f1-1b65-4b74-81ca-7034f7f7723f",
+    "alibaba": "0cc2b86d-1d45-434d-ae74-11d09ec61ae8",
+    "zoom": "99b9af0d-a833-4a5d-9e2f-8b1324f35351",
+    "acronis": "73dc0fef-1c66-4a72-9d2d-0a0f4012c175",
+    "zyxel": "96e50032-ad0d-4058-a115-4d2c13821f9f",
+    "fidelis": "11ea3a0d-87b4-4e67-a1fa-f5a6be5d6676",
+    "hitachi energy": "e383dce4-0c27-4495-91c4-0db157728d17",
+    "line": "657f3255-0560-4aed-82e4-7f579ec6acfb",
+    "incd": "a57ee1ae-c9c1-4f40-aa7b-cf10760fde3f",
+    "eset": "4a9b9929-2450-4021-b7b9-469a0255b215",
+    "synaptics": "54bb2a58-4278-44a9-851f-17e74ee51f48",
+    "tr-cert": "ca940d4e-fea4-4aa2-9a58-591a58b1ce21",
+    "netmotion": "b6533044-ea05-4482-8458-7bddeca0d079",
+    "appcheck": "2c188fdb-58e1-4908-8fce-3e437b94f1ae",
+    "snow": "ea911274-ddd9-4e68-b39a-d7d6ae8b8a65",
+    "m-files corporation": "bcf7a16e-bfdc-46e4-9e42-4187da3f4410",
+    "ncsc.ch": "455daabc-a392-441d-aa46-37d35189897c",
+    "divd": "b87402ff-ae37-4194-9dae-31abdbd6f217",
+    "devolutions": "bfee16bd-18e6-446c-9a65-f5b2e3d89c23",
+    "tha-psirt": "9d5917ae-205d-4ae5-8749-1f49479b1395",
+    "govtech csg": "1a37b84a-8e51-4525-b3d6-87e2fae01dbd",
+    "replicated": "7d06583b-61c8-4499-8067-80a92a3b48e1",
+    "netskope": "bf992f6a-e49d-4e94-9479-c4cff32c62bc",
+    "hitvan": "dce6e192-ff49-4263-9134-f0beccb9bc13",
+    "artica": "63375d6c-d89a-45ed-8ecc-c8c361b0e04c",
+    "forgerock": "6b18ae97-0aab-4af2-9ba4-74b2b139ddfa",
+    "zowe": "b1336bef-059d-4e13-b11b-9a6ef21b3c78",
+    "cert-in": "66834db9-ab24-42b4-be80-296b2e40335c",
+    "swift": "e4a1ddda-f4f5-496e-96c8-82c37d06abd0",
+    "fluid attacks": "84fe0718-d6bb-4716-a7e8-81a6d1daa869",
+    "logitech": "b573e801-1dd3-4adf-bd73-c9b814fbe067",
+    "okta": "59b22baa-87b2-4371-8e4a-e080df12f74a",
+    "anolis": "cb8f1db9-b4b1-487b-a760-f65c4f368d8e",
+    "hypr": "0dc7baee-4a9f-419f-bd0a-e21ec5dac512",
+    "bd": "2325d071-eabf-4b7b-a4ea-0819b6629a18",
+    "wdc psirt": "cb3b742e-5145-4748-b44b-5ffd45bf3b6a",
+    "zabbix": "72de3e22-0555-4a0d-ae81-9249e0f0a1e8",
+    "tv": "13430f76-86eb-43b2-a71c-82c956ef31b6",
+    "lge": "42f21055-226c-4bce-a3c8-ecf55a3551fb",
+    "ncsc-nl": "cf4a7ff5-dd38-4ede-a530-ffaa7ea59c39",
+    "silabs": "030b2754-1501-44a4-bef8-48be86a33bf4",
+    "profelis": "3039e14d-1e9f-46c2-98a6-44436e4ca5d5",
+    "citrix": "e437aed5-38e0-4fa3-a98b-cb73e7acaec6",
+    "baxter": "dba971b9-eb30-4121-91e1-3b45611354aa",
+    "palantir": "bbcbe11d-db20-4bc2-8a6e-c79f87041fd4",
+    "jetbrains": "547ada31-17d8-4964-bc5f-1b8238ba8014",
+    "nlok": "dbd8429d-f261-4b1e-94cc-ae3132817e2e",
+    "hw": "ff95705b-1a40-4639-8017-a58fa868baee",
+    "openbmc": "7eaec9cd-e18a-43f9-bb90-5f82d308c514",
+    "unisoc": "63f92e9c-2193-4c24-98a9-93640392c3d3",
+    "carrier": "e24e6442-3ae1-4538-a7b8-7ac95586db8f",
+    "medtronic": "3c337f33-b2bd-4529-9792-f48cdb2838b4",
+    "shopbeat": "d3f162d7-5820-4c58-beef-03f43baaf8ad",
+    "openharmony": "0cf5dd6e-1214-4398-a481-30441e48fafd",
+    "yugabyte": "d4ae51d3-4db5-465e-bc8a-eb6768324078",
+    "asustor1": "f35eaae9-79f2-4d0d-a5c7-7bea6ed6be77",
+    "ge gp": "2cf0fb33-79e2-44e0-beb8-826cc5ce3250",
+    "cybellum": "24e70c8d-eeca-431d-8285-199a41b4014e",
+    "rockwell": "b73dd486-f505-4403-b634-40b078b177f0",
+    "tml": "07aac9b9-e3e9-4d03-a447-764bd31371d7",
+    "sn": "303448ea-6ef3-4077-ad29-5c9bf253c375",
+    "hashicorp": "67fedba0-ff2e-4543-ba5b-aa93e87718cc",
+    "tribe29": "f7d6281c-4801-44ce-ace2-493291dedb0f",
+    "honeywell": "0dc86260-d7e3-4e81-ba06-3508e030ce8d",
+    "arm": "56a131ea-b967-4a0d-a41e-5f3549952846",
+    "knime": "296541fb-a0e3-4ca7-ab3d-683e666d143e",
+    "sailpoint": "2cfc7547-56a0-4049-8b52-c3078e8a8719",
+    "dragos": "12bdf821-1545-4a87-aac5-61670cc6fcef",
+    "zuso art": "256c161b-b921-402b-8c3b-c6c9c14d5d88",
+    "proofpoint": "d83a79dd-e128-4b83-8b64-84faf54eed46",
+    "baidu": "7b443bca-21a2-4df8-b477-ec36491d9932",
+    "securifera": "c35fbbdf-8d87-49b6-8120-920a36e62b7f",
+    "onekey": "2d533b80-6e4a-4e20-93e2-171235122846",
+    "xylem": "15c01472-ff32-4bec-916d-912e60a9fe4c",
+    "grafana": "57da9224-a3e2-4646-9d0e-c4dc2e05e7da",
+    "docker": "686469e6-3ff6-451b-ab8b-cf5b9e89401e",
+    "aha": "26969f82-7e87-44d8-9cb5-f6fb926ddd43",
+    "baicells": "084566ad-deee-4e89-acff-6b7b1bf9d4b7",
+    "opennms": "70b007e9-5235-4ee5-90b5-a71a81beeda0",
+    "canon": "f98c90f0-e9bd-4fa7-911b-51993f3571fd",
+    "b.braun": "653264ec-f98b-4e8f-b8b4-540a01b7657d",
+    "3ds": "f5a594e6-46a7-4e60-8a08-0a786e70e433",
+    "genetec": "f2b06212-cb4b-41a4-9501-fa2e367495b8",
+    "asrg": "c15abc07-96a9-4d11-a503-5d621bfe42ba",
+    "star_labs": "b1571b85-cbc9-431f-830b-0c8155323a69",
+    "honor": "3836d913-7555-4dd0-a509-f5667fdf5fe4",
+    "checkmk": "f7d6281c-4801-44ce-ace2-493291dedb0f",
+    "ox": "8ce71d90-2354-404b-a86e-bec2cc4e6981",
+    "gandc": "ace9cabe-4f4f-416b-8c39-b0e002761924",
+    "qualys": "8a309ac4-d8c7-4735-9c1d-ca39c5dfbcda",
+    "purestorage": "3895c224-4e1d-482a-adb3-fa64795683ac",
+    "panasonic corporation": "2163caeb-3942-4e93-a74b-8c75338146ce",
+    "sel": "5804bb70-792c-43e0-8596-486cc0efe699",
+    "42gears": "e5d7a401-f4d6-4559-911a-da9b371cd8ce",
+    "@huntr_ai": "c09c270a-b464-47c1-9133-acb35b22c19a",
+    "cyberdanube": "7d092a75-6bbd-48c6-a15a-0297458009bc",
+    "halborn": "79d7e77b-4bd9-4e64-ae2c-02965b1d2b73",
+    "vulncheck": "83251b91-4cc7-4094-a5c7-464a1b83ea10",
+    "phoenix": "22d9ba52-f336-4b0d-bf1f-0efbdcc3c1de",
+    "idemia": "a87f365f-9d39-4848-9b3a-58c7cae69cab",
+    "moxa": "2e0a0ee2-d866-482a-9f5e-ac03d156dbaa",
+    "liferay": "8b54e794-c6f0-462e-9faa-c1001a673ac3",
+    "ami": "7e9044f1-7f56-4c38-8864-c0c7302263d6",
+    "perforce": "ca2a266c-be2f-4d4b-92d0-47b76b1a9c4e",
+    "dotcms": "5b9d93f2-25c7-46b4-ab60-d201718c9dd8",
+    "blsops": "27b6da8a-f51d-48d9-9eef-9b7f3405d20d",
+    "panasonic_holdings_corporation": "2163caeb-3942-4e93-a74b-8c75338146ce",
+    "temporal": "61241ed8-fa44-4f23-92db-b8c443751968",
+    "sk-cert": "bc375322-d3d7-4481-b261-e29662236cfd",
+    "wolfssl": "50d2cd11-d01a-48ed-9441-5bfce9d63b27",
+    "google_devices": "83238938-5644-45f0-9007-c0392bcf6222",
+    "mim": "f77a3b2e-a86f-4a4e-80d3-3d1378fbb974",
+    "opencloudos": "03686db1-8513-43c4-b487-c6ad847afd3a",
+    "progresssoftware": "f9fea0b6-671e-4eea-8fde-31911902ae05",
+    "vulsec": "2fdefc65-d750-4b8d-96ee-6e2c0c42dbfe",
+    "xi": "902ff664-2e36-43e3-a1aa-3210c82d1b67",
+    "samsung.tv_appliance": "ca193ba2-0cff-4e34-b04e-1ea07103c6fe",
+    "payara": "769c9ae7-73c3-4e47-ae19-903170fc3eb8",
+    "pandorafms": "63375d6c-d89a-45ed-8ecc-c8c361b0e04c",
+    "cert-pl": "4bb8329e-dd38-46c1-aafb-9bf32bcb93c6",
+    "samsungmobile": "3af57064-a867-422c-b2ad-40307b65c458",
+    "softiron": "0a72a055-908d-47f5-a16a-1f09049c16c6",
+    "1e": "4a68d2b9-b68a-4765-95bd-17f35092666b",
+    "algosec": "ca5f073f-8266-4d43-b3e3-6eb0bb18a738",
+    "sec-vlab": "551230f0-3615-47bd-b7cc-93e92e730bbf",
+    "asr": "68630edc-a58c-4cbd-9b01-0e130455c8ae",
+    "cisa-cg": "9119a7d8-5eab-497f-8521-727c672e3725",
+    "mandiant": "027e81ed-0dd4-4685-ab4d-884aec5bb484",
+    "ni": "bca5b2e8-03a4-4781-b4ca-c6a078c0bfd4",
+    "greenrocketsecurity": "6a7d5611-ec52-41f4-b33a-a457deec1320",
+    "lexmark": "7bc73191-a2b6-4c63-9918-753964601853",
+    "hanwha_vision": "fc9afe74-3f80-4fb7-a313-e6f036a89882",
+    "oda": "8a9629cb-c5e7-4d2a-a894-111e8039b7ea",
+    "illumio": "ea070963-4842-4451-bceb-906a17f8dc69",
+    "psf": "28c92f92-d60d-412d-b760-e73465c3df22",
+    "yokogawagroup": "7168b535-132a-4efe-a076-338f829b2eb9",
+    "papercut": "eb41dac7-0af8-4f84-9f6d-0272772514f4",
+    "dfinity": "6b35d637-e00f-4228-858c-b20ad6e1d07b",
+    "sra": "57dba5dd-1a03-47f6-8b36-e84e47d335d8",
+    "nx": "96d4e157-0bf0-48b3-8efd-382c68caf4e0",
+    "wso2": "ed10eef1-636d-4fbe-9993-6890dfa878f8",
+    "fortra": "df4dee71-de3a-4139-9588-11b62fe6c0ff",
+    "manageengine": "0fc0942c-577d-436f-ae8e-945763c79b02",
+    "elan": "36106deb-8e95-420b-a0a0-e70af5d245df",
+    "curl": "2499f714-1537-4658-8207-48ae4bb9eae9",
+    "postgresql": "f86ef6dc-4d3a-42ad-8f28-e6d5547a5007",
+    "sonatype": "103e4ec9-0a87-450b-af77-479448ddef11",
+    "concretecms": "ff5b8ace-8b95-4078-9743-eac1ca5451de",
+    "bt": "13061848-ea10-403d-bd75-c83a022c2891",
+    "ly-corporation": "657f3255-0560-4aed-82e4-7f579ec6acfb",
+    "hiddenlayer": "6f8de1f0-f67e-45a6-b68f-98777fdb759c",
+    "ciena": "7bd90cf1-1651-495e-9ae8-9415fb3c9feb",
+    "libreswan": "d42dc95b-23f1-4e06-9076-20753a0fb0df"
+}
diff --git a/vdb/lib/config.py b/vdb/lib/config.py
index 04615b4..5199183 100644
--- a/vdb/lib/config.py
+++ b/vdb/lib/config.py
@@ -25,20 +25,20 @@
     os.makedirs(CACHE_DIR)
 
 # Binary db file
-VDB_BIN_FILE = os.path.join(DATA_DIR, "data.vdb5")
+VDB_BIN_FILE = os.path.join(DATA_DIR, "data.vdb6")
 
 # Binary DB index file
-VDB_BIN_INDEX = os.path.join(DATA_DIR, "data.index.vdb5")
+VDB_BIN_INDEX = os.path.join(DATA_DIR, "data.index.vdb6")
 
 # NPM advisory url
 NPM_SERVER = "https://registry.npmjs.org"
 NPM_AUDIT_URL = NPM_SERVER + "/-/npm/v1/security/audits"
 NPM_ADVISORIES_URL = NPM_SERVER + "/-/npm/v1/security/advisories"
 
-NPM_APP_INFO = {"name": "appthreat-vdb", "version": "5.0.0"}
+NPM_APP_INFO = {"name": "appthreat-vdb", "version": "6.0.0"}
 
 CVE_TPL = """
-{"cve":{"data_type":"CVE","data_format":"MITRE","data_version":"4.0","CVE_data_meta":{"ID":"%(cve_id)s","ASSIGNER":"%(assigner)s"},"problemtype":{"problemtype_data":[{"description":[{"lang":"en","value":"%(cwe_id)s"}]}]},"references":{"reference_data": %(references)s},"description":{"description_data":[{"lang":"en","value":"%(description)s"}]}},"configurations":{"CVE_data_version":"4.0","nodes":[{"operator":"OR","cpe_match":[{"vulnerable":true,"cpe23Uri":"cpe:2.3:a:%(vendor)s:%(product)s:%(version)s:*:%(edition)s:*:*:*:*:*","versionStartExcluding":"%(version_start_excluding)s","versionEndExcluding":"%(version_end_excluding)s","versionStartIncluding":"%(version_start_including)s","versionEndIncluding":"%(version_end_including)s"}, {"vulnerable":false,"cpe23Uri":"cpe:2.3:a:%(vendor)s:%(product)s:%(fix_version_start_including)s:*:%(edition)s:*:*:*:*:*","versionStartExcluding":"%(fix_version_start_excluding)s","versionEndExcluding":"%(fix_version_end_excluding)s","versionStartIncluding":"%(fix_version_start_including)s","versionEndIncluding":"%(fix_version_end_including)s"}]}]},"impact":{"baseMetricV3":{"cvssV3":{"version":"3.1","vectorString":"%(vectorString)s","attackVector":"NETWORK","attackComplexity":"%(attackComplexity)s","privilegesRequired":"NONE","userInteraction":"REQUIRED","scope":"UNCHANGED","confidentialityImpact":"%(severity)s","integrityImpact":"%(severity)s","availabilityImpact":"%(severity)s","baseScore":%(score).1f,"baseSeverity":"%(severity)s"},"exploitabilityScore":%(exploitabilityScore).1f,"impactScore":%(score).1f},"baseMetricV2":{"cvssV2":{"version":"2.0","vectorString":"AV:N/AC:M/Au:N/C:P/I:P/A:P","accessVector":"NETWORK","accessComplexity":"MEDIUM","authentication":"NONE","confidentialityImpact":"PARTIAL","integrityImpact":"PARTIAL","availabilityImpact":"PARTIAL","baseScore":%(score).1f},"severity":"%(severity)s","exploitabilityScore":%(exploitabilityScore).1f,"impactScore":%(score).1f,"acInsufInfo":false,"obtainAllPrivilege":false,"obtainUserPrivilege":false,"obtainOtherPrivilege":false,"userInteractionRequired":false}},"publishedDate":"%(publishedDate)s","lastModifiedDate":"%(lastModifiedDate)s"}
+{"cve":{"data_type":"CVE","data_format":"MITRE","data_version":"4.0","CVE_data_meta":{"ID":"%(cve_id)s","ASSIGNER":"%(assigner)s"},"problemtype":{"problemtype_data":[{"description":[{"lang":"en","value":"%(cwe_id)s"}]}]},"references":{"reference_data": %(references)s},"description":{"description_data":[{"lang":"en","value":"%(description)s"}]}},"configurations":{"CVE_data_version":"4.0","nodes":[{"operator":"OR","cpe_match":[{"vulnerable":true,"cpe23Uri":"cpe:2.3:a:%(vendor)s:%(product)s:%(version)s:*:%(edition)s:*:*:*:*:*","versionStartExcluding":"%(version_start_excluding)s","versionEndExcluding":"%(version_end_excluding)s","versionStartIncluding":"%(version_start_including)s","versionEndIncluding":"%(version_end_including)s"}, {"vulnerable":false,"cpe23Uri":"cpe:2.3:a:%(vendor)s:%(product)s:%(fix_version_start_including)s:*:%(edition)s:*:*:*:*:*","versionStartExcluding":"%(fix_version_start_excluding)s","versionEndExcluding":"%(fix_version_end_excluding)s","versionStartIncluding":"%(fix_version_start_including)s","versionEndIncluding":"%(fix_version_end_including)s"}]}]},"impact":{"baseMetricV3":{"cvssV3":{"version":"3.1","vectorString":"%(vectorString)s","attackVector":"NETWORK","attackComplexity":"%(attackComplexity)s","privilegesRequired":"NONE","userInteraction":"%(userInteraction)s","scope":"UNCHANGED","confidentialityImpact":"%(severity)s","integrityImpact":"%(severity)s","availabilityImpact":"%(severity)s","baseScore":%(score).1f,"baseSeverity":"%(severity)s"},"exploitabilityScore":%(exploitabilityScore).1f,"impactScore":%(score).1f},"baseMetricV2":{"cvssV2":{"version":"2.0","vectorString":"AV:N/AC:M/Au:N/C:P/I:P/A:P","accessVector":"NETWORK","accessComplexity":"MEDIUM","authentication":"NONE","confidentialityImpact":"PARTIAL","integrityImpact":"PARTIAL","availabilityImpact":"PARTIAL","baseScore":%(score).1f},"severity":"%(severity)s","exploitabilityScore":%(exploitabilityScore).1f,"impactScore":%(score).1f,"acInsufInfo":false,"obtainAllPrivilege":false,"obtainUserPrivilege":false,"obtainOtherPrivilege":false,"userInteractionRequired":false}},"publishedDate":"%(publishedDate)s","lastModifiedDate":"%(lastModifiedDate)s"}
 """
 
 osv_url_dict = {
@@ -56,15 +56,16 @@
     "pub": "https://osv-vulnerabilities.storage.googleapis.com/Pub/all.zip",
     "uvi": "https://osv-vulnerabilities.storage.googleapis.com/UVI/all.zip",
     "github": "https://osv-vulnerabilities.storage.googleapis.com/GitHub%20Actions/all.zip",
-    "android": "https://osv-vulnerabilities.storage.googleapis.com/Android/all.zip",
     "alpine": "https://osv-vulnerabilities.storage.googleapis.com/Alpine/all.zip",
     "debian": "https://osv-vulnerabilities.storage.googleapis.com/Debian/all.zip",
     "cran": "https://osv-vulnerabilities.storage.googleapis.com/CRAN/all.zip",
     "almalinux": "https://osv-vulnerabilities.storage.googleapis.com/AlmaLinux/all.zip",
     "rockylinux": "https://osv-vulnerabilities.storage.googleapis.com/Rocky%20Linux/all.zip",
     "swift": "https://osv-vulnerabilities.storage.googleapis.com/SwiftURL/all.zip",
+    "git": "https://osv-vulnerabilities.storage.googleapis.com/GIT/all.zip",
 }
 
+# These feeds introduce too much false positives
 if os.getenv("OSV_INCLUDE_FUZZ"):
     osv_url_dict[
         "linux"
@@ -72,6 +73,7 @@
     osv_url_dict[
         "oss-fuzz"
     ] = "https://osv-vulnerabilities.storage.googleapis.com/OSS-Fuzz/all.zip"
+    osv_url_dict["android"] = "https://osv-vulnerabilities.storage.googleapis.com/Android/all.zip",
 
 VULN_LIST_URL = "https://github.com/appthreat/vuln-list/archive/refs/heads/main.zip"
 
@@ -81,14 +83,12 @@
     nvd_exclude_types = os.getenv("NVD_EXCLUDE_TYPES", "").split(",")
 
 # Placeholder fix version to use to indicate max versions
-# Leave lowercase since used by depscan
-placeholder_fix_version = "99.99.9"
+PLACEHOLDER_FIX_VERSION = "99.99.9"
 
 # Placeholder exclude version to use to indicate non-vulnerability
 # This is highly important for debian where a specific distro may be non-vulnerable
 # While CPEs are the correct method of representing the exclusion, this version hack is aimed to be a short workaround
-# Leave lowercase since used by depscan
-placeholder_exclude_version = "88.88.8"
+PLACEHOLDER_EXCLUDE_VERSION = "88.88.8"
 
 # How many CVEs should be packed and written to the db file as a unit
 # A large value here requires a larger max_buffer_size. Else could lead to msgpack.exceptions.BufferFull exceptions during read
@@ -96,3 +96,32 @@
 
 # Limits size of unpacked data
 MAX_BUFFER_SIZE = 200 * 1024 * 1024  # 200 MiB
+
+THREAT_TO_SEVERITY = {
+    "unspecified": "LOW",
+    "": "LOW",
+    "negligible": "LOW",
+    "low": "LOW",
+    "unimportant": "LOW",
+    "severity_low": "LOW",
+    "medium": "MEDIUM",
+    "severity_medium": "MEDIUM",
+    "moderate": "MEDIUM",
+    "severity_moderate": "MEDIUM",
+    "important": "HIGH",
+    "high": "HIGH",
+    "severity_important": "HIGH",
+    "critical": "CRITICAL",
+    "severity_critical": "CRITICAL",
+}
+
+VENDOR_TO_VERS_SCHEME = {
+    "almalinux": "rpm",
+    "ubuntu": "deb",
+    "debian": "deb",
+    "suse": "rpm",
+    "redhat": "rpm",
+    "opensuse": "rpm",
+    "alpine": "apk",
+    "gentoo": "ebuild"
+}
diff --git a/vdb/lib/cve.py b/vdb/lib/cve.py
index 9468714..3d6f4e6 100644
--- a/vdb/lib/cve.py
+++ b/vdb/lib/cve.py
@@ -1,15 +1,22 @@
+import os
 import uuid
 
+import orjson
 from semver import VersionInfo
 
 from vdb.lib import (
-    CPE_REGEX,
+    config,
+    CPE_FULL_REGEX,
     KNOWN_PKG_TYPES,
+    PKG_TYPES_MAP,
     Vulnerability,
     VulnerabilityDetail,
     VulnerabilitySource,
 )
+from vdb.lib import db6 as db_lib
+from vdb.lib.cna import ASSIGNER_UUID_MAP
 from vdb.lib.cve_model import (
+    CVE,
     CVE1,
     Affected,
     CnaPublishedContainer,
@@ -24,6 +31,7 @@
     Language,
     Metrics,
     Metrics1,
+    Metrics2,
     OrgId,
     ProblemType,
     ProblemTypes,
@@ -37,9 +45,13 @@
     Version,
     Versions,
 )
-from vdb.lib.cve_model.cvss_v3 import Field1
+from vdb.lib.cve_model.common import CiaType
+from vdb.lib.cve_model.cvss_v3 import Field0, Field1
+from vdb.lib.utils import calculate_hash, to_purl_vers
 
-ASSIGNER_UUID_MAP = {}
+# Our DB creation process could result in duplicates. By tracking these keys we reduce them
+source_completed_keys = {}
+index_completed_keys = {}
 
 
 def to_cve_metadata(avuln: Vulnerability):
@@ -48,6 +60,7 @@ def to_cve_metadata(avuln: Vulnerability):
         if ASSIGNER_UUID_MAP.get(avuln.assigner):
             assigner_org_id = ASSIGNER_UUID_MAP[avuln.assigner]
         else:
+            # We have encountered a new assigner
             assigner_org_id = OrgId(UuidType(str(uuid.uuid4())))
             ASSIGNER_UUID_MAP[avuln.assigner] = assigner_org_id
     else:
@@ -56,6 +69,7 @@ def to_cve_metadata(avuln: Vulnerability):
         cveId=CveId(avuln.id),
         state=State.PUBLISHED,
         assignerOrgId=assigner_org_id,
+        assignerShortName=avuln.assigner
     )
     metadata.datePublished = avuln.source_orig_time
     metadata.dateUpdated = avuln.source_update_time
@@ -101,7 +115,7 @@ def to_product_versions(vendor, adetail: VulnerabilityDetail) -> list[Versions]:
             versions.append(
                 Versions(
                     version=Version(adetail.mii),
-                    lessThan=Version(adetail.mai),
+                    lessThan=Version(adetail.mae),
                     versionType=version_type,
                     status=Status.affected,
                 )
@@ -111,6 +125,7 @@ def to_product_versions(vendor, adetail: VulnerabilityDetail) -> list[Versions]:
             versions.append(
                 Versions(
                     version=Version(adetail.mii),
+                    lessThanOrEqual=Version(adetail.mai),
                     versionType=version_type,
                     status=Status.affected,
                 )
@@ -127,7 +142,7 @@ def to_product_versions(vendor, adetail: VulnerabilityDetail) -> list[Versions]:
         if adetail.mai and adetail.mai not in ("*", "-"):
             versions.append(
                 Versions(
-                    version=Version(adetail.mai),
+                    version=Version("0"),
                     lessThanOrEqual=Version(adetail.mai),
                     versionType=version_type,
                     status=Status.affected,
@@ -136,7 +151,7 @@ def to_product_versions(vendor, adetail: VulnerabilityDetail) -> list[Versions]:
         if adetail.mae and adetail.mae not in ("*", "-"):
             versions.append(
                 Versions(
-                    version=Version(adetail.mae),
+                    version=Version("0"),
                     lessThan=Version(adetail.mae),
                     versionType=version_type,
                     status=Status.affected,
@@ -150,21 +165,119 @@ def to_cve_affected(avuln: Vulnerability) -> Affected | None:
     adetail: VulnerabilityDetail
     for adetail in avuln.details:
         cpe_uri = adetail.cpe_uri
-        parts = CPE_REGEX.match(cpe_uri)
-        versions = to_product_versions(parts.group("vendor"), adetail)
-        if parts and versions:
-            p = Product(
-                vendor=parts.group("vendor"),
-                product=parts.group("package"),
-                packageName=parts.group("package"),
-                cpes=[Cpe(cpe_uri)],
-                defaultStatus=Status.unknown,
-                versions=versions,
-            )
-            products.append(p)
+        parts = CPE_FULL_REGEX.match(cpe_uri)
+        if parts:
+            versions = to_product_versions(parts.group("vendor"), adetail)
+            if versions:
+                # Similar to purl type
+                vendor = parts.group("vendor")
+                # Similar to purl namespace
+                product = parts.group("package")
+                # Similar to purl name
+                package_name = parts.group("package")
+                if "/" in product:
+                    tmp_a = product.split("/")
+                    if len(tmp_a) != 2:
+                        if len(tmp_a) > 2 and vendor in ("generic", "swift"):
+                            product = os.path.dirname(product)
+                            package_name = os.path.basename(package_name)
+                            # If we get an empty package_name then fallback to using the full string as package_name
+                            if not package_name:
+                                product = None
+                                package_name = parts.group("package")
+                        else:
+                            product = None
+                    elif vendor not in ("golang",):
+                        product = tmp_a[0]
+                        package_name = tmp_a[1]
+                # Product and package name are the same.
+                # For some ecosystems, we can remove the product (namespace) to rely only on name
+                # For others, we can make the product the same as vendor
+                if product == package_name:
+                    if vendor in ("npm", "pypi", "gem", "swift"):
+                        product = None
+                    elif vendor not in ("maven", "composer", "generic"):
+                        product = vendor
+                # See if we can substitute vers scheme
+                if config.VENDOR_TO_VERS_SCHEME.get(vendor):
+                    vendor = config.VENDOR_TO_VERS_SCHEME.get(vendor)
+                # This prevents cargo:cargo or nuget:nuget
+                if product == vendor and vendor in KNOWN_PKG_TYPES:
+                    product = None
+                p = Product(
+                    vendor=vendor,
+                    product=product,
+                    packageName=package_name,
+                    cpes=[Cpe(cpe_uri)],
+                    defaultStatus=Status.unknown,
+                    versions=versions,
+                )
+                products.append(p)
     return Affected(products) if products else None
 
 
+def to_complexity_type(complexity: str) -> str:
+    if complexity and complexity.lower() == "low":
+        return "LOW"
+    return "HIGH"
+
+
+def severity_to_impact(severity: str) -> CiaType:
+    """Method to convert severity string to a valid impact enum string"""
+    if not severity:
+        return CiaType.NONE
+    if severity.lower() in ("critical", "high", "medium", "moderate"):
+        return CiaType.HIGH
+    return CiaType.LOW
+
+
+def to_metrics(avuln: Vulnerability) -> Metrics:
+    metrics_list = []
+    if avuln.cvss_v3.vector_string and avuln.cvss_v3.vector_string.startswith("CVSS:3.1"):
+        metrics_list.append(Metrics1(
+            cvssV3_1=Field1(
+                version="3.1",
+                baseScore=avuln.cvss_v3.base_score,
+                baseSeverity=config.THREAT_TO_SEVERITY.get(avuln.severity.lower()),
+                vectorString=avuln.cvss_v3.vector_string,
+                attackVector=avuln.cvss_v3.attack_vector,
+                attackComplexity=to_complexity_type(avuln.cvss_v3.attack_complexity),
+                privilegesRequired=avuln.cvss_v3.privileges_required,
+                userInteraction=avuln.cvss_v3.user_interaction,
+                scope=avuln.cvss_v3.scope,
+                confidentialityImpact=severity_to_impact(avuln.cvss_v3.confidentiality_impact),
+                integrityImpact=severity_to_impact(avuln.cvss_v3.integrity_impact),
+                availabilityImpact=severity_to_impact(avuln.cvss_v3.availability_impact),
+            )
+        ))
+    elif avuln.cvss_v3.vector_string and avuln.cvss_v3.vector_string.startswith("CVSS:3.0"):
+        metrics_list.append(Metrics2(
+            cvssV3_0=Field0(
+                version="3.0",
+                baseScore=avuln.cvss_v3.base_score,
+                baseSeverity=config.THREAT_TO_SEVERITY.get(avuln.severity.lower()),
+                vectorString=avuln.cvss_v3.vector_string,
+                attackVector=avuln.cvss_v3.attack_vector,
+                attackComplexity=avuln.cvss_v3.attack_complexity,
+                privilegesRequired=avuln.cvss_v3.privileges_required,
+                userInteraction=avuln.cvss_v3.user_interaction,
+                scope=avuln.cvss_v3.scope,
+                confidentialityImpact=severity_to_impact(avuln.cvss_v3.confidentiality_impact),
+                integrityImpact=severity_to_impact(avuln.cvss_v3.integrity_impact),
+                availabilityImpact=severity_to_impact(avuln.cvss_v3.availability_impact),
+            )
+        ))
+
+    return Metrics(
+        metrics_list
+    )
+
+
+def to_references(avuln: Vulnerability) -> list[Reference] | None:
+    ref_list = [Reference(url=url) for url in avuln.related_urls if url.startswith("http")]
+    return References(ref_list) if ref_list else None
+
+
 def to_cve_containers(avuln: Vulnerability) -> CnaPublishedContainer | None:
     provier_meta = ProviderMetadata(
         orgId=ASSIGNER_UUID_MAP.get(avuln.assigner, OrgId(UuidType(str(uuid.uuid4()))))
@@ -177,51 +290,32 @@ def to_cve_containers(avuln: Vulnerability) -> CnaPublishedContainer | None:
         providerMetadata=provier_meta,
         descriptions=[Description(lang=Language("en"), value=avuln.description)],
         affected=affected,
-        metrics=Metrics(
-            [
-                Metrics1(
-                    cvssV3_1=Field1(
-                        version="3.1",
-                        baseScore=avuln.cvss_v3.base_score,
-                        baseSeverity=avuln.severity,
-                        vectorString=avuln.cvss_v3.vector_string,
-                        attackVector=avuln.cvss_v3.attack_vector,
-                        attackComplexity=avuln.cvss_v3.attack_complexity,
-                        privilegesRequired=avuln.cvss_v3.privileges_required,
-                        userInteraction=avuln.cvss_v3.user_interaction,
-                        scope=avuln.cvss_v3.scope,
-                        confidentialityImpact=avuln.cvss_v3.confidentiality_impact,
-                        integrityImpact=avuln.cvss_v3.integrity_impact,
-                        availabilityImpact=avuln.cvss_v3.availability_impact,
-                    )
-                )
-            ]
-        ),
-        references=References([Reference(url=url) for url in avuln.related_urls]),
+        metrics=to_metrics(avuln),
     )
+    references = to_references(avuln)
+    if references:
+        cont.references = references
     cont.dateAssigned = avuln.source_orig_time
     # CWE
     if avuln.problem_type and "noinfo" not in avuln.problem_type:
         problem_types = []
         for acwe in avuln.problem_type.split(","):
-            problem_types.append(
-                ProblemTypes(
-                    [
-                        ProblemType(
-                            descriptions=[
-                                Description1(
-                                    lang=Language("en"),
-                                    description=acwe,
-                                    cweId=acwe,
-                                    type="CWE",
-                                )
-                            ]
-                        )
-                    ]
+            # Check if this starts with CWE
+            if acwe.startswith("CWE"):
+                problem_types.append(
+                    ProblemType(
+                        descriptions=[
+                            Description1(
+                                lang=Language("en"),
+                                description=acwe,
+                                cweId=acwe,
+                                type="CWE",
+                            )
+                        ]
+                    )
                 )
-            )
         if problem_types:
-            cont.problemTypes = problem_types
+            cont.problemTypes = ProblemTypes(problem_types)
     return cont
 
 
@@ -230,14 +324,18 @@ class CVESource(VulnerabilitySource):
     Generic CVE source that uses the CVE 5.0 models
     """
 
-    db = None
+    db_conn = None
+    index_conn = None
+
+    def __init__(self):
+        self.db_conn, self.index_conn = db_lib.get(db_file=config.VDB_BIN_FILE, index_file=config.VDB_BIN_INDEX)
 
     @classmethod
-    def download_all(cls, local_store=True):
-        pass
+    def download_all(cls):
+        db_lib.clear_all()
 
     @classmethod
-    def download_recent(cls, local_store=True):
+    def download_recent(cls):
         pass
 
     @classmethod
@@ -248,7 +346,10 @@ def bulk_search(cls, app_info, pkg_list):
     def refresh(cls):
         pass
 
-    def convert(self, data: list[Vulnerability]) -> list[CVE1]:
+    def convert(self, cve_data: dict) -> list[Vulnerability]:
+        pass
+
+    def convert5(self, data: list[Vulnerability]) -> list[CVE]:
         cves = []
         for avuln in data:
             containers = Containers(cna=to_cve_containers(avuln))
@@ -262,5 +363,56 @@ def convert(self, data: list[Vulnerability]) -> list[CVE1]:
                 cves.append(cve_obj)
         return cves
 
-    def store(self, data: list[CVE1]):
-        pass
+    def store(self, data: list[Vulnerability]):
+        """Store data in the database"""
+        cve5_list = self.convert5(data)
+        self.store5(cve5_list)
+
+    def store5(self, data: list[CVE]):
+        """Store the CVE and index data into the SQLite database"""
+        with self.db_conn as dbc:
+            with self.index_conn as indexc:
+                for d in data:
+                    cve_id = d.cveMetadata.cveId
+                    cve_id = cve_id.model_dump(mode="python")
+                    source_data = d.model_dump(mode="json",
+                                               exclude_defaults=True,
+                                               exclude_unset=True,
+                                               exclude_none=True)
+                    source_data_str = orjson.dumps(source_data).decode("utf-8", "ignore")
+                    source_hash = calculate_hash(source_data_str)
+                    if d.containers.cna and d.containers.cna.affected:
+                        for affected in d.containers.cna.affected.root:
+                            vers = to_purl_vers(affected.vendor, affected.versions)
+                            purl_type = affected.vendor if PKG_TYPES_MAP.get(affected.vendor) else "generic"
+                            purl_prefix = f"""pkg:{purl_type}/"""
+                            if affected.product:
+                                purl_prefix = f"{purl_prefix}{affected.product}/"
+                            purl_prefix = f"{purl_prefix}{affected.packageName}"
+                            pkg_key = f"{cve_id}|{affected.vendor}|{affected.product}|{affected.packageName}|{source_hash}"
+                            index_pkg_key = f"{cve_id}|{affected.vendor}|{affected.product}|{affected.packageName}|{vers}"
+                            # Filter obvious duplicates
+                            if not source_completed_keys.get(pkg_key):
+                                dbc.execute(
+                                    "INSERT INTO cve_data values(?, ?, ?, ?, jsonb(?), ?, ?);", (
+                                        cve_id,
+                                        affected.vendor,
+                                        affected.product,
+                                        affected.packageName,
+                                        source_data_str,
+                                        None,
+                                        source_hash
+                                    ))
+                                source_completed_keys[pkg_key] = True
+                            if not index_completed_keys.get(index_pkg_key):
+                                indexc.execute(
+                                    "INSERT INTO cve_index values(?, ?, ?, ?, ?, ?);", (
+                                        cve_id,
+                                        affected.vendor,
+                                        affected.product,
+                                        affected.packageName,
+                                        vers,
+                                        purl_prefix
+                                    )
+                                )
+                                index_completed_keys[index_pkg_key] = True
diff --git a/vdb/lib/cve_model/__init__.py b/vdb/lib/cve_model/__init__.py
index cec56af..b84e048 100644
--- a/vdb/lib/cve_model/__init__.py
+++ b/vdb/lib/cve_model/__init__.py
@@ -53,8 +53,9 @@ class ReferenceTags(Enum):
     vdb_entry = "vdb-entry"
 
 
+# Removed patten validation to support a wide range of CVE id
 class CveId(RootModel[str]):
-    root: Annotated[str, Field(pattern="^CVE-[0-9]{4}-[0-9]{4,19}$")]
+    root: Annotated[str, Field(min_length=4, max_length=128)]
 
 
 class OrgId(RootModel[UuidType]):
@@ -86,16 +87,6 @@ class ShortName(RootModel[str]):
     ]
 
 
-class Timestamp(RootModel[AwareDatetime]):
-    root: Annotated[
-        AwareDatetime,
-        Field(
-            description="Date/time format based on RFC3339 and ISO ISO8601, with an optional timezone in the format 'yyyy-MM-ddTHH:mm:ssZZZZ'. If timezone offset is not given, GMT (0000) is assumed.",
-            pattern="^(((2000|2400|2800|(19|2[0-9](0[48]|[2468][048]|[13579][26])))-02-29)|(((19|2[0-9])[0-9]{2})-02-(0[1-9]|1[0-9]|2[0-8]))|(((19|2[0-9])[0-9]{2})-(0[13578]|10|12)-(0[1-9]|[12][0-9]|3[01]))|(((19|2[0-9])[0-9]{2})-(0[469]|11)-(0[1-9]|[12][0-9]|30)))T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(\\.[0-9]+)?(Z|[+-][0-9]{2}:[0-9]{2})?$",
-        ),
-    ]
-
-
 class Version(RootModel[str]):
     root: Annotated[
         str,
@@ -113,6 +104,7 @@ class Status(Enum):
     unknown = "unknown"
 
 
+# Removed pattern validation since it wasn't working for go full names
 class Cpe(RootModel[str]):
     root: Annotated[
         str,
@@ -120,7 +112,6 @@ class Cpe(RootModel[str]):
             description="Common Platform Enumeration (CPE) Name in either 2.2 or 2.3 format",
             max_length=2048,
             min_length=1,
-            pattern="([c][pP][eE]:/[AHOaho]?(:[A-Za-z0-9._\\-~%]*){0,6})|(cpe:2\\.3:[aho*\\-](:(((\\?*|\\*?)([a-zA-Z0-9\\-._]|(\\\\[\\\\*?!\"#$%&'()+,/:;<=>@\\[\\]\\^`{|}~]))+(\\?*|\\*?))|[*\\-])){5}(:(([a-zA-Z]{2,3}(-([a-zA-Z]{2}|[0-9]{3}))?)|[*\\-]))(:(((\\?*|\\*?)([a-zA-Z0-9\\-._]|(\\\\[\\\\*?!\"#$%&'()+,/:;<=>@\\[\\]\\^`{|}~]))+(\\?*|\\*?))|[*\\-])){4})",
             title="CPE Name",
         ),
     ]
@@ -131,7 +122,6 @@ class Module(RootModel[str]):
         str,
         Field(
             description="Name of the affected component, feature, module, sub-component, sub-product, API, command, utility, program, or functionality (optional).",
-            max_length=4096,
             min_length=1,
         ),
     ]
@@ -153,7 +143,6 @@ class ProgramRoutine(BaseModel):
         str,
         Field(
             description="Name of the affected source code file, function, method, subroutine, or procedure.",
-            max_length=4096,
             min_length=1,
         ),
     ]
@@ -524,7 +513,7 @@ class CveMetadataPublished(BaseModel):
         Field(None, description="The user that requested the CVE identifier."),
     ]
     dateUpdated: Annotated[
-        Optional[Timestamp],
+        Optional[AwareDatetime],
         Field(None, description="The date/time the record was last updated."),
     ]
     serial: Annotated[
@@ -536,14 +525,14 @@ class CveMetadataPublished(BaseModel):
         ),
     ]
     dateReserved: Annotated[
-        Optional[Timestamp],
+        Optional[AwareDatetime],
         Field(
             None,
             description="The date/time this CVE ID was reserved in the CVE automation workgroup services system. Disclaimer: This date reflects when the CVE ID was reserved, and does not necessarily indicate when this vulnerability was discovered, shared with the affected vendor, publicly disclosed, or updated in CVE.",
         ),
     ]
     datePublished: Annotated[
-        Optional[Timestamp],
+        Optional[AwareDatetime],
         Field(
             None,
             description="The date/time the CVE Record was first published in the CVE List.",
@@ -585,23 +574,23 @@ class CveMetadataRejected(BaseModel):
         ),
     ]
     dateUpdated: Annotated[
-        Optional[Timestamp],
+        Optional[AwareDatetime],
         Field(None, description="The date/time the record was last updated."),
     ]
     datePublished: Annotated[
-        Optional[Timestamp],
+        Optional[AwareDatetime],
         Field(
             None,
             description="The date/time the CVE Record was first published in the CVE List.",
         ),
     ]
     dateRejected: Annotated[
-        Optional[Timestamp],
+        Optional[AwareDatetime],
         Field(None, description="The date/time the CVE ID was rejected."),
     ]
     state: Annotated[State1, Field(description="State of CVE - PUBLISHED, REJECTED.")]
     dateReserved: Annotated[
-        Optional[Timestamp],
+        Optional[AwareDatetime],
         Field(
             None,
             description="The date/time this CVE ID was reserved in the CVE automation workgroup services system. Disclaimer: This date reflects when the CVE ID was reserved, and does not necessarily indicate when this vulnerability was discovered, shared with the affected vendor, publicly disclosed, or updated in CVE.",
@@ -618,7 +607,7 @@ class ProviderMetadata(BaseModel):
         Field(None, description="The container provider's organizational short name."),
     ]
     dateUpdated: Annotated[
-        Optional[Timestamp],
+        Optional[AwareDatetime],
         Field(
             None,
             description="Timestamp to be set by the system of record at time of submission. If dateUpdated is provided to the system of record it will be replaced by the current timestamp at the time of submission.",
@@ -808,7 +797,7 @@ class Description(BaseModel):
     )
     lang: Language
     value: Annotated[
-        str, Field(description="Plain text description.", max_length=4096, min_length=1)
+        str, Field(description="Plain text description.", min_length=1)
     ]
     supportingMedia: Annotated[
         Optional[List[SupportingMediaItem]],
@@ -878,7 +867,6 @@ class Scenario(BaseModel):
         str,
         Field(
             description="Description of the scenario this metrics object applies to. If no specific scenario is given, GENERAL is used as the default and applies when no more specific metric matches.",
-            max_length=4096,
             min_length=1,
         ),
     ]
@@ -925,7 +913,7 @@ class Exploits(RootModel[List[Description]]):
 
 class TimelineItem(BaseModel):
     time: Annotated[
-        Timestamp,
+        AwareDatetime,
         Field(
             description="Timestamp representing when the event in the timeline occurred. The timestamp format is based on RFC3339 and ISO ISO8601, with an optional timezone. yyyy-MM-ddTHH:mm:ssZZZZ - if the timezone offset is not given, GMT (0000) is assumed."
         ),
@@ -937,7 +925,7 @@ class TimelineItem(BaseModel):
         ),
     ]
     value: Annotated[
-        str, Field(description="A summary of the event.", max_length=4096, min_length=1)
+        str, Field(description="A summary of the event.", min_length=1)
     ]
 
 
@@ -1029,7 +1017,6 @@ class Description1(BaseModel):
         str,
         Field(
             description="Text description of problemType, or title from CWE or OWASP.",
-            max_length=4096,
             min_length=1,
         ),
     ]
@@ -1225,14 +1212,14 @@ class CnaPublishedContainer(BaseModel):
     )
     providerMetadata: ProviderMetadata
     dateAssigned: Annotated[
-        Optional[Timestamp],
+        Optional[AwareDatetime],
         Field(
             None,
             description="The date/time this CVE ID was associated with a vulnerability by a CNA.",
         ),
     ]
     datePublic: Annotated[
-        Optional[Timestamp],
+        Optional[AwareDatetime],
         Field(
             None,
             description="If known, the date/time the vulnerability was disclosed publicly.",
@@ -1250,7 +1237,7 @@ class CnaPublishedContainer(BaseModel):
     descriptions: Descriptions
     affected: Affected
     problemTypes: Optional[ProblemTypes] = None
-    references: References
+    references: Optional[References] = None
     impacts: Optional[Impacts] = None
     metrics: Optional[Metrics] = None
     configurations: Optional[Configurations] = None
@@ -1273,7 +1260,7 @@ class AdpContainer(BaseModel):
     )
     providerMetadata: ProviderMetadata
     datePublic: Annotated[
-        Optional[Timestamp],
+        Optional[AwareDatetime],
         Field(
             None,
             description="If known, the date/time the vulnerability was disclosed publicly.",
diff --git a/vdb/lib/db.py b/vdb/lib/db.py
deleted file mode 100644
index 7df64e5..0000000
--- a/vdb/lib/db.py
+++ /dev/null
@@ -1,314 +0,0 @@
-from collections import defaultdict
-
-from vdb.lib import config, storage
-from vdb.lib.utils import convert_to_occurrence, parse_cpe, parse_purl, version_compare
-
-INDEX_DATA = {}
-VENDOR_INDEX_DATA = {}
-POS_INDEX_DATA = {}
-
-
-def build_index(index_pos_list):
-    """This function builds two index. One with just name and version the other
-    including vendor (aka group) string
-
-    :param index_pos_list:
-    :return: Normal index and vendor index
-    """
-    idx = defaultdict(list)
-    vendor_idx = defaultdict(list)
-    pos_idx = defaultdict(list)
-    for dp in index_pos_list:
-        store_pos = dp.get("store_pos")
-        store_end_pos = dp.get("store_end_pos")
-        for d in dp.get("index_list"):
-            cve_id = d.get("id")
-            min_version = d.get("mie")
-            if (not min_version or min_version == "*") and d.get("mii"):
-                min_version = d.get("mii")
-            max_version = d.get("mae")
-            if (not max_version or max_version == "*") and d.get("mai"):
-                max_version = d.get("mai")
-            if not min_version:
-                min_version = "0"
-            if not max_version:
-                max_version = "*"
-            ver_range_str = f"{min_version}-{max_version}|{cve_id}"
-            idx[d["name"]].append(ver_range_str)
-            store_pos_key = f"{store_pos}_{store_end_pos}|{cve_id}"
-            pos_idx[d["name"]].append(store_pos_key)
-            if d.get("vendor"):
-                vendor_idx_key = f'{d.get("vendor")}|{d["name"]}'
-                vendor_idx[vendor_idx_key].append(ver_range_str)
-                pos_idx[vendor_idx_key].append(store_pos_key)
-    return idx, vendor_idx, pos_idx
-
-
-def get(db_file=config.VDB_BIN_FILE, index_file=config.VDB_BIN_INDEX):
-    """Get database instance
-
-    :param db_file: DB file
-    :param index_file: Index file
-    :return: DB and index file
-    """
-    global INDEX_DATA, VENDOR_INDEX_DATA, POS_INDEX_DATA
-    INDEX_DATA, VENDOR_INDEX_DATA, POS_INDEX_DATA = build_index(
-        storage.stream_read(index_file)
-    )
-    return {"db_file": db_file, "index_file": index_file}
-
-
-def store(db, datas, reindex=True):
-    """Store data in the table
-
-    :param db: Table instance
-    :param datas: Data list to store
-    :param reindex: Boolean to enable re-index
-    :return: Stored packed documents
-    """
-    if datas is None:
-        return None
-    global INDEX_DATA, VENDOR_INDEX_DATA, POS_INDEX_DATA
-    docs = storage.store(datas, db_file=db["db_file"], index_file=db["index_file"])
-    if reindex:
-        # Re-read the index
-        INDEX_DATA, VENDOR_INDEX_DATA, POS_INDEX_DATA = build_index(
-            storage.stream_read(db["index_file"])
-        )
-    return docs
-
-
-def list_all(db):
-    """Method to return all data
-
-    :param db: db instance
-    :return: List of data stored
-    """
-    return storage.stream_read(db["db_file"])
-
-
-def list_all_occurrence(db):
-    """Method to return all data
-
-    :param db: db instance
-    :return: List of data stored as occurrences
-    """
-    return convert_to_occurrence(storage.stream_read(db["db_file"]))
-
-
-def index_count(index_file=config.VDB_BIN_INDEX):
-    """
-    Method to return the number of indexed items
-    :param index_file: Index DB file
-    :return: Count of the index
-    """
-    return len(storage.stream_read(index_file))
-
-
-def _key_func(data, match_list):
-    """Test function for package search
-
-    :param version_attrib: Version value from the db
-    :param value: Value to compare against
-    :return: True if there is a match False otherwise
-    """
-    if isinstance(data, dict):
-        if not data.get("details"):
-            return False
-        cpe_uri = data["details"].get("cpe_uri")
-        package = data["details"].get("package")
-        min_affected_version_including = data["details"].get("mii", "0")
-        min_affected_version_excluding = data["details"].get("mie", None)
-        max_affected_version_including = data["details"].get("mai", "*")
-        max_affected_version_excluding = data["details"].get("mae", None)
-    else:
-        cpe_uri = data.details.cpe_uri
-        package = data.details.package
-        min_affected_version_including = data.details.mii
-        max_affected_version_including = data.details.mai
-        min_affected_version_excluding = data.details.mie
-        max_affected_version_excluding = data.details.mae
-    if not cpe_uri:
-        return False
-    vendor, _, _, _ = parse_cpe(cpe_uri)
-    for match in match_list:
-        name_ver = match.split("|")
-        # Search by name and version
-        if len(name_ver) == 2:
-            # Check if we have a hit
-            if name_ver[0] == package and version_compare(
-                name_ver[1],
-                min_affected_version_including,
-                max_affected_version_including,
-                min_affected_version_excluding,
-                max_affected_version_excluding,
-            ):
-                return True
-        # Search by pos or vendor, name and version
-        if len(name_ver) == 3:
-            # Is name_ver[0] pos?
-            if "_" in name_ver[0]:
-                if name_ver[1].lower() == package.lower() and version_compare(
-                    name_ver[2],
-                    min_affected_version_including,
-                    max_affected_version_including,
-                    min_affected_version_excluding,
-                    max_affected_version_excluding,
-                ):
-                    return True
-            else:
-                # Check if we have a hit
-                if (
-                    name_ver[0] == vendor
-                    and name_ver[1].lower() == package.lower()
-                    and version_compare(
-                        name_ver[2],
-                        min_affected_version_including,
-                        max_affected_version_including,
-                        min_affected_version_excluding,
-                        max_affected_version_excluding,
-                    )
-                ):
-                    return True
-        # Search by pos, vendor, name and version
-        if len(name_ver) == 4:
-            # Check if we have a hit
-            if (
-                name_ver[1] == vendor
-                and name_ver[2].lower() == package.lower()
-                and version_compare(
-                    name_ver[3],
-                    min_affected_version_including,
-                    max_affected_version_including,
-                    min_affected_version_excluding,
-                    max_affected_version_excluding,
-                )
-            ):
-                return True
-    return False
-
-
-def bulk_index_search(pkg_list):
-    """"""
-    ret_list = set()
-    pos_cve_cache = {}
-    for pkg in pkg_list:
-        # This key could be either a vendor|name or name
-        pkg_type = None
-        if pkg.get("purl"):
-            purl_obj = parse_purl(pkg.get("purl"))
-            # Retain empty group names
-            vendor = purl_obj.get("namespace")
-            pkg_type = purl_obj.get("type")
-            name = purl_obj.get("name")
-            if not vendor:
-                if pkg_type in ("npm",):
-                    # Search for empty vendor in the key
-                    vendor = ""
-                else:
-                    # Fallback to using type as the vendor
-                    vendor = pkg_type
-            if pkg_type == "swift":
-                vendor = "swift"
-                name = f'{purl_obj.get("namespace")}/{purl_obj.get("name")}'
-            # Handle invalid purl that could lead to name becoming None
-            # See #81
-            if not name:
-                name = pkg.get("name")
-            version = purl_obj.get("version")
-            # cdxgen uses unspecified if version is unknown
-            if version and version == "unspecified":
-                version = "*"
-        else:
-            vendor = pkg.get("vendor")
-            name = pkg.get("name")
-            version = pkg.get("version")
-        vendor_idx_key = (
-            f"{vendor.lower()}|{name.lower()}" if vendor else f"{name.lower()}"
-        )
-        # If there is vendor information use it to perform strict search
-        # For ecosystem such as npm, always perform such a strict search to reduce false positives
-        if vendor or (pkg_type and pkg_type in ("npm",)):
-            version_list = VENDOR_INDEX_DATA.get(vendor_idx_key, [])
-            store_pos_cve = POS_INDEX_DATA.get(vendor_idx_key)
-        else:
-            version_list = INDEX_DATA.get(vendor_idx_key, [])
-            store_pos_cve = POS_INDEX_DATA.get(vendor_idx_key)
-        if store_pos_cve:
-            for sp in store_pos_cve:
-                tmp_a = sp.split("|")
-                pos_cve_cache[tmp_a[1]] = tmp_a[0]
-        for vers in version_list:
-            cve_id = ""
-            tmp_a = vers.split("|")
-            if len(tmp_a) > 1:
-                cve_id = tmp_a[1]
-            vrange = tmp_a[0].split("-")
-            minver = vrange[0]
-            maxver = vrange[1]
-            if not minver:
-                minver = "0"
-            store_pos = pos_cve_cache.get(cve_id)
-            if version_compare(version, minver, maxver):
-                ret_list.add(f"{store_pos}|{vendor_idx_key}|{version}")
-    return list(ret_list)
-
-
-def index_search(name, version):
-    """Search the index for the given package name and version
-
-    :param name: Name of the package
-    :param version: Package version
-
-    :return boolean True if the package should be found on the main database. False otherwise.
-    """
-    try:
-        datas = bulk_index_search([{"name": name.lower(), "version": version}])
-        return len(datas) > 0
-    except IndexError:
-        return False
-
-
-def pkg_search(db, name, version):
-    """Search for a given package and convert into Vulnerability Occurence
-
-    :param db: db instance
-    :param name: Name of the package
-    :param version: Package version
-
-    :return List of vulnerability occurrence or none
-    """
-    datas = storage.stream_bulk_search(
-        [name.lower() + "|" + version], _key_func, db_file=db["db_file"]
-    )
-    return convert_to_occurrence(datas)
-
-
-def vendor_pkg_search(db, vendor, name, version):
-    """Search for a given package and convert into Vulnerability Occurence
-
-    :param db: db instance
-    :param vendor: Vendor name
-    :param name: Name of the package
-    :param version: Package version
-
-    :return List of vulnerability occurrence or none
-    """
-    datas = storage.stream_bulk_search(
-        [vendor.lower() + "|" + name.lower() + "|" + version],
-        _key_func,
-        db_file=db["db_file"],
-    )
-    return convert_to_occurrence(datas)
-
-
-def pkg_bulk_search(db, pkg_key_list):
-    """Bulk search for a given package and convert into Vulnerability Occurence
-
-    :param db: db instance
-    :param pkg_key_list: List of pos|package name|version keys
-
-    :return List of vulnerability occurence or none
-    """
-    datas = storage.stream_bulk_search(pkg_key_list, _key_func, db_file=db["db_file"])
-    return convert_to_occurrence(datas)
diff --git a/vdb/lib/db6.py b/vdb/lib/db6.py
new file mode 100644
index 0000000..e429a7d
--- /dev/null
+++ b/vdb/lib/db6.py
@@ -0,0 +1,84 @@
+import os
+import sys
+
+import apsw
+
+from vdb.lib import config
+
+db_conn: apsw.Connection = None
+index_conn: apsw.Connection = None
+tables_created = False
+DB_FILE_SEP = "///" if sys.platform == "win32" else "//"
+
+
+def ensure_schemas(db_conn_obj: apsw.Connection, index_conn_obj: apsw.Connection):
+    """Create the sqlite tables and indexes in case they don't exist"""
+    db_conn_obj.execute(
+        "CREATE TABLE if not exists cve_data(cve_id TEXT NOT NULL, type TEXT NOT NULL, namespace TEXT, name TEXT NOT NULL, source_data BLOB NOT NULL, override_data BLOB, source_data_hash TEXT NOT NULL);")
+    db_conn_obj.pragma("synchronous", "OFF")
+    db_conn_obj.pragma("journal_mode", "MEMORY")
+    index_conn_obj.execute(
+        "CREATE TABLE if not exists cve_index(cve_id TEXT NOT NULL, type TEXT NOT NULL, namespace TEXT, name TEXT NOT NULL, vers TEXT NOT NULL, purl_prefix TEXT NOT NULL);")
+    index_conn_obj.pragma("synchronous", "OFF")
+    index_conn_obj.pragma("journal_mode", "MEMORY")
+
+
+def get(db_file: str = config.VDB_BIN_FILE, index_file: str = config.VDB_BIN_INDEX, read_only=False) -> (
+        apsw.Connection, apsw.Connection):
+    global db_conn, index_conn, tables_created
+    if not db_file.startswith("file:"):
+        db_file = f"file:{DB_FILE_SEP}{os.path.abspath(db_file)}"
+    if not index_file.startswith("file:"):
+        index_file = f"file:{DB_FILE_SEP}{os.path.abspath(index_file)}"
+    flags = apsw.SQLITE_OPEN_URI | apsw.SQLITE_OPEN_NOFOLLOW | (apsw.SQLITE_OPEN_READONLY if read_only else apsw.SQLITE_OPEN_CREATE | apsw.SQLITE_OPEN_READWRITE)
+    if not db_conn:
+        db_conn = apsw.Connection(db_file, flags=flags)
+    if not index_conn:
+        index_conn = apsw.Connection(index_file, flags=flags)
+    if not tables_created:
+        ensure_schemas(db_conn, index_conn)
+        tables_created = True
+    return db_conn, index_conn
+
+
+def stats():
+    cve_data_count = 0
+    res = db_conn.execute("SELECT count(*) FROM cve_data").fetchone()
+    if res:
+        cve_data_count = res[0]
+    cve_index_count = 0
+    res = index_conn.execute("SELECT count(*) FROM cve_index").fetchone()
+    if res:
+        cve_index_count = res[0]
+    return cve_data_count, cve_index_count
+
+
+def clear_all():
+    if db_conn:
+        db_conn.execute("DELETE FROM cve_data;")
+    if index_conn:
+        index_conn.execute("DELETE FROM cve_index;")
+
+
+def optimize_and_close_all():
+    """
+    Safely close the connections by creating indexes and vacuuming if needed.
+    """
+    if db_conn:
+        db_conn.execute(
+            "CREATE INDEX if not exists idx1 on cve_data(cve_id, type);")
+        db_conn.execute("VACUUM;")
+        db_conn.close()
+    if index_conn:
+        index_conn.execute(
+            "CREATE INDEX if not exists cidx1 on cve_index(cve_id);")
+        index_conn.execute(
+            "CREATE INDEX if not exists cidx2 on cve_index(type, namespace, name);")
+        index_conn.execute(
+            "CREATE INDEX if not exists cidx3 on cve_index(type, name);")
+        index_conn.execute(
+            "CREATE INDEX if not exists cidx4 on cve_index(namespace, name);")
+        index_conn.execute(
+            "CREATE INDEX if not exists cidx5 on cve_index(purl_prefix);")
+        index_conn.execute("VACUUM;")
+        index_conn.close()
diff --git a/vdb/lib/gha.py b/vdb/lib/gha.py
index 0c57063..df3e695 100755
--- a/vdb/lib/gha.py
+++ b/vdb/lib/gha.py
@@ -17,7 +17,7 @@
 
 from vdb.lib import config
 from vdb.lib.nvd import NvdSource
-from vdb.lib.utils import compress_str, get_default_cve_data
+from vdb.lib.utils import compress_str, get_default_cve_data, get_cvss3_from_vector
 
 logging.basicConfig(
     level=logging.INFO, format="%(levelname)s [%(asctime)s] %(message)s"
@@ -28,6 +28,14 @@
 headers = {"Authorization": f"token {api_token}"}
 
 
+vendor_overrides = {
+    "pip": "pypi",
+    "go": "golang",
+    "rust": "cargo",
+    "rubygems": "gem"
+}
+
+
 def get_query(qtype="recent"):
     """"""
     if qtype == "recent" or not qtype:
@@ -85,23 +93,22 @@ def get_query(qtype="recent"):
 class GitHubSource(NvdSource):
     """GitHub CVE source"""
 
-    def download_all(self, local_store=True):
+    def download_all(self):
         """Download all historic cve data"""
         data_list = []
         last_id = None
         for y in range(0, int(config.GHA_PAGES_COUNT)):
             data, page_info = self.fetch(vtype=last_id)
             if data:
-                if local_store:
-                    self.store(data)
+                self.store(data)
             if page_info and page_info["hasNextPage"]:
                 last_id = page_info["endCursor"]
         return data_list
 
-    def download_recent(self, local_store=True):
+    def download_recent(self):
         """Method which downloads the recent CVE"""
         data, _ = self.fetch("recent")
-        if data and local_store:
+        if data:
             self.store(data)
         return data
 
@@ -177,7 +184,7 @@ def convert(self, cve_data):
             if cve.get("withdrawnAt"):
                 continue
             cve_id = None
-            assigner = "cve@mitre.org"
+            assigner = "mitre"
             references = []
             for r in cve["references"]:
                 references.append({"url": r["url"], "name": r["url"]})
@@ -186,23 +193,24 @@ def convert(self, cve_data):
                     cve_id = cid["value"]
             if not cve_id:
                 cve_id = cve["ghsaId"]
-                assigner = "@github"
+                assigner = "github_m"
             references = orjson.dumps(references, option=orjson.OPT_NAIVE_UTC)
             if isinstance(references, bytes):
                 references = references.decode("utf-8", "ignore")
             for p in cve["vulnerabilities"]["nodes"]:
                 if not p:
                     continue
-                vendor = p["package"]["ecosystem"]
+                vendor = p["package"]["ecosystem"].lower()
                 product = p["package"]["name"]
-                if ":" in product or "/" in product:
+                vendor = vendor_overrides.get(vendor, vendor)
+                if vendor not in ("golang", "swift", "composer") and (":" in product or "/" in product):
                     tmp_a = re.split(r"[/|:]", product)
                     # This extract's the correct vendor based on the namespace
                     # Eg: org.springframework:spring-webflux would result in
-                    # vendor: org.springframework
-                    # product: spring-webflux
-                    vendor = tmp_a[0]
-                    product = tmp_a[len(tmp_a) - 1]
+                    # product: org.springframework/spring-webflux
+                    product = f"{tmp_a[0]}/{tmp_a[len(tmp_a) - 1]}"
+                if vendor not in ("nuget",):
+                    product = product.lower()
                 version = p["vulnerableVersionRange"]
                 (
                     version_start_including,
@@ -220,7 +228,6 @@ def convert(self, cve_data):
                     fix_version_start_excluding,
                     fix_version_end_excluding,
                 ) = self.get_version_range(fix_version)
-
                 severity = p["severity"]
                 (
                     score,
@@ -229,6 +236,14 @@ def convert(self, cve_data):
                     attack_complexity,
                 ) = get_default_cve_data(severity)
                 exploitability_score = score
+                cvss3_obj = get_cvss3_from_vector(vector_string)
+                attack_complexity = ""
+                user_interaction = "REQUIRED"
+                if cvss3_obj:
+                    exploitability_score = cvss3_obj.get("temporalScore")
+                    attack_complexity = cvss3_obj.get("attackComplexity")
+                    score = cvss3_obj.get("baseScore")
+                    user_interaction = cvss3_obj.get("userInteraction")
                 description = """# {}
 {}
             """.format(
@@ -257,6 +272,7 @@ def convert(self, cve_data):
                     severity=severity,
                     attackComplexity=attack_complexity,
                     score=score,
+                    userInteraction=user_interaction,
                     exploitabilityScore=exploitability_score,
                     publishedDate=cve["publishedAt"],
                     lastModifiedDate=cve["updatedAt"],
diff --git a/vdb/lib/npm.py b/vdb/lib/npm.py
index daa65f5..d08ae61 100644
--- a/vdb/lib/npm.py
+++ b/vdb/lib/npm.py
@@ -15,6 +15,7 @@
     convert_md_references,
     convert_to_occurrence,
     fix_text,
+    get_cvss3_from_vector,
     get_default_cve_data,
     serialize_vuln_list,
 )
@@ -67,7 +68,7 @@ def fetch(self, payload):
         json_data = r.json()
         return self.convert(json_data)
 
-    def download_recent(self, local_store=True):
+    def download_recent(self):
         """Method which downloads the recent CVE"""
         client = httpx.Client(http2=True, follow_redirects=True, timeout=180)
         url = config.NPM_ADVISORIES_URL + "?perPage=100&page=1"
@@ -75,12 +76,12 @@ def download_recent(self, local_store=True):
         if r.ok:
             json_data = r.json()
             data = self.convert(json_data.get("objects"))
-            if data and local_store:
+            if data:
                 self.store(data)
             return data
         return []
 
-    def download_all(self, local_store=True):
+    def download_all(self):
         """Download all historic cve data"""
         data_list = []
         client = httpx.Client(http2=True, follow_redirects=True, timeout=180)
@@ -90,7 +91,7 @@ def download_all(self, local_store=True):
             if r.ok:
                 json_data = r.json()
                 data = self.convert(json_data.get("objects"))
-                if data and local_store:
+                if data:
                     self.store(data)
                 data_list += data
                 if json_data.get("urls", {}).get("next"):
@@ -165,7 +166,7 @@ def convert(self, adv_data):
         return ret_data
 
     def to_vuln(self, v, ret_data):
-        assigner = "@npm"
+        assigner = "github_m"
         # Iterate the cve list if available
         cves = v.get("cves")
         if not cves:
@@ -197,15 +198,22 @@ def to_vuln(self, v, ret_data):
                 severity
             )
             cvss = v.get("cvss")
+            user_interaction = "REQUIRED"
+            exploitability_score = score
+            metadata = v.get("metadata", {})
+            if isinstance(metadata, dict) and metadata.get("exploitability"):
+                exploitability_score = metadata.get("exploitability")
             if cvss:
                 if cvss.get("score"):
                     score = cvss.get("score")
                 if cvss.get("vectorString"):
                     vector_string = cvss.get("vectorString")
-            exploitability_score = score
-            metadata = v.get("metadata", {})
-            if isinstance(metadata, dict) and metadata.get("exploitability"):
-                exploitability_score = metadata.get("exploitability")
+                    if vector_string:
+                        cvss3_obj = get_cvss3_from_vector(vector_string)
+                        if cvss3_obj:
+                            exploitability_score = cvss3_obj.get("temporalScore")
+                            attack_complexity = cvss3_obj.get("attackComplexity")
+                            user_interaction = cvss3_obj.get("userInteraction")
             cwe_id = v.get("cwe")
             findings = v.get("findings")
             used_version = ""
@@ -262,6 +270,7 @@ def to_vuln(self, v, ret_data):
                     severity=severity,
                     attackComplexity=attack_complexity,
                     score=score,
+                    userInteraction=user_interaction,
                     exploitabilityScore=exploitability_score,
                     publishedDate=published_date,
                     lastModifiedDate=last_modified_date,
diff --git a/vdb/lib/nvd.py b/vdb/lib/nvd.py
index ac401db..7bc6993 100644
--- a/vdb/lib/nvd.py
+++ b/vdb/lib/nvd.py
@@ -11,10 +11,9 @@
     CvssV3,
     Vulnerability,
     VulnerabilityDetail,
-    VulnerabilitySource,
     config,
 )
-from vdb.lib import db as db_lib
+from vdb.lib.cve import CVESource
 
 logging.basicConfig(
     level=logging.INFO, format="%(levelname)s [%(asctime)s] %(message)s"
@@ -41,30 +40,23 @@ def get_version(inc_version: str, exc_version: str) -> str:
     return exc_version
 
 
-class NvdSource(VulnerabilitySource):
+class NvdSource(CVESource):
     """NVD CVE source. This uses CVE json 1.1 format that are split based on the year"""
 
-    db = None
-
-    def download_all(self, local_store=True):
+    def download_all(self):
         """Download all historic cve data"""
-        # Create database
-        self.db = db_lib.get()
-        data_list = []
+        super.download_all()
         for y in range(now.year, int(start_year) - 1, -1):
             data = self.fetch(y)
             if not data:
                 continue
-            if local_store:
-                self.store(data)
-        return data_list
+            self.store(data)
 
-    def download_recent(self, local_store=True):
+    def download_recent(self):
         """Method which downloads the recent CVE gzip from NVD"""
         # Create database
-        self.db = db_lib.get()
         data = self.fetch("recent")
-        if data and local_store:
+        if data:
             self.store(data)
         return data
 
@@ -116,13 +108,6 @@ def refresh(self):
         """Refresh CVE data"""
         return self.download_all()
 
-    def store(self, data: list[Vulnerability], reindex=True):
-        """Store data in the database"""
-        if not self.db:
-            self.db = db_lib.get()
-        docs = db_lib.store(self.db, data, reindex)
-        return docs
-
     def bulk_search(self, app_info, pkg_list):
         """
         Bulk search the resource instead of downloading the information
@@ -137,8 +122,8 @@ def convert_vuln(vuln: dict) -> Vulnerability | None:
         # CWE
         problem_type = ""
         if (
-            vuln["cve"]["problemtype"]["problemtype_data"]
-            and vuln["cve"]["problemtype"]["problemtype_data"][0]["description"]
+                vuln["cve"]["problemtype"]["problemtype_data"]
+                and vuln["cve"]["problemtype"]["problemtype_data"][0]["description"]
         ):
             problem_type = vuln["cve"]["problemtype"]["problemtype_data"][0][
                 "description"
@@ -242,6 +227,9 @@ def convert_api_vuln_detail(vuln: dict) -> list[VulnerabilityDetail] | None:
         """Convert configurations section from the API into details"""
         config_list = vuln.get("configurations", [])
         details = []
+        rdata = vuln.get("references", [])
+        git_urls = [r["url"].lower() for r in rdata if "git" in r["url"]]
+        parsed_git_repo_names: dict[str, bool] = {}
         for aconfig in config_list:
             cpe_list = []
             nodes = aconfig.get("nodes", [])
@@ -278,6 +266,22 @@ def convert_api_vuln_detail(vuln: dict) -> list[VulnerabilityDetail] | None:
                     "source_orig_time": vuln["published"],
                 }
                 cpe_details_list.append(detail)
+                # Try to extract any git references from related urls
+                # See: https://github.com/AppThreat/vulnerability-db/issues/91
+                if git_urls:
+                    for agit_url in git_urls:
+                        git_repo_name = agit_url
+                        if "github.com" in agit_url and "cve" not in agit_url:
+                            for part in ("/issues", "/commit", "/pull", "/blob", "/releases", "/wiki", "/security", "/compare"):
+                                if part in agit_url:
+                                    git_repo_name = agit_url.split(part)[0]
+                        if git_repo_name and not parsed_git_repo_names.get(git_repo_name):
+                            parsed_git_repo_names[git_repo_name] = True
+                            git_repo_name = git_repo_name.removeprefix("https://").removeprefix("http://").removeprefix("git@").removesuffix("/")
+                            cpe_uri = f"cpe:2.3:a:generic:{git_repo_name}:*:*:*:*:*:*:*:*"
+                            new_git_detail = detail.copy()
+                            new_git_detail["cpe_uri"] = cpe_uri
+                            cpe_details_list.append(new_git_detail)
             for det in cpe_details_list:
                 adetail = VulnerabilityDetail.from_dict(det)
                 # Include only the application details
diff --git a/vdb/lib/osv.py b/vdb/lib/osv.py
index 2d8ae20..068fbc1 100644
--- a/vdb/lib/osv.py
+++ b/vdb/lib/osv.py
@@ -22,33 +22,31 @@
 # Size of the stream to read and write to the file
 DOWNLOAD_CHUNK_SIZE = 4096
 
-
 vendor_overrides = {
     "apk": "alpine",
     "deb": "debian",
     "go": "golang",
-    "crates.io": "crates",
+    "crates.io": "cargo",
     "swifturl": "swift",
+    "github actions": "github"
 }
 
 
 class OSVSource(NvdSource):
     """OSV CVE source"""
 
-    def download_all(self, local_store=True):
+    def download_all(self):
         """Download all cve data"""
         # For performance do not retain the whole data in-memory
         # See: https://github.com/AppThreat/vulnerability-db/issues/27
         data_list = []
         for _, url in config.osv_url_dict.items():
             data = self.fetch(url)
-            if not data:
-                continue
-            if local_store:
+            if data:
                 self.store(data)
         return data_list
 
-    def download_recent(self, local_store=True):
+    def download_recent(self):
         pass
 
     def fetch(self, url):
@@ -128,15 +126,16 @@ def to_vuln(cve_data):
                 if i.startswith("CVE"):
                     cve_id = i
                     break
-        assigner = "OSV"
+        assigner = "google"
         vector_string = ""
         if cve_id.startswith("GHSA"):
-            assigner = "@github"
+            assigner = "github_m"
         elif cve_id.startswith("CVE"):
-            assigner = "cve@mitre.org"
+            assigner = "mitre"
         elif cve_id.startswith("NPM"):
-            assigner = "@npm"
-        severity = "LOW"
+            assigner = "github_m"
+        # For malwares, default to critical
+        severity = "CRITICAL" if cve_id.startswith("MAL") else "LOW"
         if cve_data.get("severity"):
             severity_list = cve_data.get("severity")
             for sv in severity_list:
@@ -175,12 +174,14 @@ def to_vuln(cve_data):
                         if cvss.get("score"):
                             score = cvss.get("score")
                             severity = convert_score_severity(score)
+            user_interaction = "REQUIRED"
             if vector_string:
                 cvss3_obj = get_cvss3_from_vector(vector_string)
                 score = cvss3_obj.get("baseScore")
                 severity = cvss3_obj.get("baseSeverity")
                 exploitability_score = cvss3_obj.get("temporalScore")
                 attack_complexity = cvss3_obj.get("attackComplexity")
+                user_interaction = cvss3_obj.get("userInteraction")
             else:
                 (
                     score,
@@ -188,6 +189,10 @@ def to_vuln(cve_data):
                     dvector_string,
                     attack_complexity,
                 ) = get_default_cve_data(severity)
+                # Override the score for malware
+                if cve_id.startswith("MAL"):
+                    score = 10.0
+                    user_interaction = "NONE"
                 # Set the default vector string only if unavailable
                 if not vector_string and dvector_string:
                     vector_string = dvector_string
@@ -200,29 +205,33 @@ def to_vuln(cve_data):
             pkg_name_list = []
             purl = parse_purl(pkg_data.get("package", {}).get("purl", ""))
             if purl:
-                if purl.get("namespace"):
-                    vendor = purl["namespace"]
-                if purl.get("name"):
+                if purl.get("type"):
+                    vendor = purl["type"]
+                if purl.get("namespace") and purl.get("name"):
+                    pkg_name = f'{purl["namespace"]}/{purl["name"]}'
+                elif purl.get("name"):
                     pkg_name = purl["name"]
-            if ":" in pkg_name and vendor not in ("swift", "swifturl"):
+            if ":" in pkg_name and vendor.lower() not in ("swift", "swifturl", "github", "github actions"):
+                # Example: commons-fileupload:commons-fileupload
+                # org.apache.tomcat:tomcat
                 tmp_a = pkg_name.split(":")
                 if len(tmp_a) == 2:
-                    vendor = tmp_a[0]
-                    pkg_name = tmp_a[-1]
+                    pkg_name = f"{tmp_a[0]}/{tmp_a[-1]}"
             # In case of swift, we need to remove any https protocol from the name
             if pkg_name.startswith("https://"):
                 pkg_name = pkg_name.removeprefix("https://")
-            pkg_name_list.append(pkg_name)
+            if pkg_name:
+                pkg_name_list.append(pkg_name)
             # Since swift allows both url and local based lookups, we store both the variations
-            if vendor in ("swift", "swifturl") and pkg_name.startswith("github.com"):
+            if vendor in ("swift", "swifturl", "github", "github actions") and pkg_name.startswith("github.com"):
                 pkg_name_list.append(pkg_name.removeprefix("github.com/"))
             # For OS packages, such as alpine OSV appends the os version to the vendor
             # Let's remove it and add it to package name
             if ":" in vendor_ecosystem and (
-                "alpine" in vendor
-                or "debian" in vendor
-                or "almalinux" in vendor
-                or "rocky" in vendor
+                    "alpine" in vendor
+                    or "debian" in vendor
+                    or "almalinux" in vendor
+                    or "rocky" in vendor
             ):
                 tmp_v = vendor_ecosystem.split(":")
                 vendor = tmp_v[0].lower().replace(" ", "").replace("-", "")
@@ -235,10 +244,10 @@ def to_vuln(cve_data):
                 edition = f"{vendor}-{vdistro}"
                 # Only use the precise version for os packages
                 if (
-                    "debian" in vendor
-                    or "alpine" in vendor
-                    or "almalinux" in vendor
-                    or "rocky" in vendor
+                        "debian" in vendor
+                        or "alpine" in vendor
+                        or "almalinux" in vendor
+                        or "rocky" in vendor
                 ):
                     pkg_name_list = [f"{edition}/{pkg_name}"]
                 else:
@@ -290,6 +299,7 @@ def to_vuln(cve_data):
                         severity=severity,
                         attackComplexity=attack_complexity,
                         score=score,
+                        userInteraction=user_interaction,
                         exploitabilityScore=exploitability_score,
                         publishedDate=cve_data.get("published"),
                         lastModifiedDate=cve_data.get("modified"),
@@ -301,6 +311,15 @@ def to_vuln(cve_data):
                     except Exception:
                         pass
             for r in ranges:
+                if r.get("type") == "GIT" and r.get("repo"):
+                    repo_name = (r.get("repo").removeprefix("http://")
+                                 .removeprefix("https://")
+                                 .removeprefix("git://")
+                                 .removesuffix("/")
+                                 .removesuffix(".git")
+                                 .lower())
+                    pkg_name_list.append(repo_name)
+                    vendor = "generic"
                 events = r.get("events")
                 rversions_list = r.get("versions", [])
                 version_end_including = ""
@@ -314,8 +333,8 @@ def to_vuln(cve_data):
                 if rversions_list:
                     version_start_including = rversions_list[0]
                     if (
-                        len(rversions_list) > 1
-                        and version_start_including != rversions_list[-1]
+                            len(rversions_list) > 1
+                            and version_start_including != rversions_list[-1]
                     ):
                         version_end_including = rversions_list[-1]
                 for ev in events:
@@ -344,10 +363,10 @@ def to_vuln(cve_data):
                         version_end_excluding = ev.get("limit").split(":")[-1]
                     # Create an entry for each introduced + fixed/limit event
                     if version_start_including and (
-                        fix_version_start_including
-                        or version_end_including
-                        or version_end_excluding
-                        or (len(events) == 1 and not versions_list)
+                            fix_version_start_including
+                            or version_end_including
+                            or version_end_excluding
+                            or (len(events) == 1 and not versions_list)
                     ):
                         for full_pkg in pkg_name_list:
                             tdata = config.CVE_TPL % dict(
@@ -372,6 +391,7 @@ def to_vuln(cve_data):
                                 severity=severity,
                                 attackComplexity=attack_complexity,
                                 score=score,
+                                userInteraction=user_interaction,
                                 exploitabilityScore=exploitability_score,
                                 publishedDate=cve_data.get("published"),
                                 lastModifiedDate=cve_data.get("modified"),
diff --git a/vdb/lib/search.py b/vdb/lib/search.py
new file mode 100644
index 0000000..73f79f0
--- /dev/null
+++ b/vdb/lib/search.py
@@ -0,0 +1,122 @@
+from typing import Any
+
+import orjson
+
+from vdb.lib import db6, utils
+from vdb.lib.cve_model import CVE
+
+
+def _filter_hits(raw_hits: list, compare_ver: str) -> list:
+    filtered_list = []
+    for ahit in raw_hits:
+        cve_id = ahit[0]
+        vers = ahit[-1]
+        if utils.vers_compare(compare_ver, vers):
+            filtered_list.append({
+                "cve_id": cve_id,
+                "type": ahit[1],
+                "namespace": ahit[2],
+                "name": ahit[3],
+                "vers": vers
+            })
+    return filtered_list
+
+
+def get_cve_data(db_conn, index_hits: list[dict, Any], search_str: str) -> list[dict[str, str | CVE | None]]:
+    """Get CVE data for the index results
+
+    Args:
+        db_conn: DB Connection or None to create a new one
+        index_hits: Hits from one of the search methods
+        search_str: Original search string used
+
+    Returns:
+        list: List of CVE data with original source data as a pydantic model
+    """
+    if not db_conn:
+        db_conn, _ = db6.get(read_only=True)
+    data_list = []
+    for ahit in index_hits:
+        results = exec_query(db_conn,
+                             "SELECT cve_id, type, namespace, name, json_object('source', source_data), json_object('override', override_data) FROM cve_data WHERE cve_id = ? AND type = ? ORDER BY cve_id DESC;",
+                             (ahit["cve_id"], ahit["type"]))
+        for res in results:
+            data_list.append({
+                "cve_id": res[0],
+                "type": res[1],
+                "namespace": res[2],
+                "name": res[3],
+                "matching_vers": ahit["vers"],
+                "matched_by": search_str,
+                "source_data": CVE.model_validate(orjson.loads(res[4])["source"], strict=False) if res[4] else None,
+                "override_data": orjson.loads(res[5])["override"] if res[5] else None
+            })
+    return data_list
+
+
+def search_by_cpe_like(cpe: str, with_data=False) -> list | None:
+    """Search by CPE or colon-separate strings"""
+    db_conn, index_conn = db6.get(read_only=True)
+    if cpe.startswith("cpe:"):
+        vendor, package, version, _ = utils.parse_cpe(cpe)
+    elif cpe.count(":") == 2:
+        vendor, package, version = cpe.split(":")
+    else:
+        return None
+    raw_hits = exec_query(index_conn,
+                          "SELECT cve_id, type, namespace, name, vers FROM cve_index where namespace = ? AND name = ?;",
+                          (vendor, package))
+    if not raw_hits:
+        raw_hits = exec_query(index_conn,
+                              "SELECT cve_id, type, namespace, name, vers FROM cve_index where type = ? AND name = ?;",
+                              (vendor, package))
+    filtered_list = _filter_hits(raw_hits, version)
+    if with_data:
+        return get_cve_data(db_conn, filtered_list, cpe)
+    return filtered_list
+
+
+def search_by_purl_like(purl: str, with_data=False) -> list | None:
+    """Search by purl like string"""
+    db_conn, index_conn = db6.get(read_only=True)
+    purl_obj = utils.parse_purl(purl)
+    if purl_obj:
+        ptype = purl_obj.get("type")
+        namespace = purl_obj.get("namespace")
+        name = purl_obj.get("name")
+        version = purl_obj.get("version", "*")
+        purl_prefix = f"pkg:{ptype}/"
+        if namespace:
+            purl_prefix = f"{purl_prefix}{namespace}/"
+        purl_prefix = f"{purl_prefix}{name}"
+        args = [purl_prefix, ptype]
+        # We enforce a strict search for None
+        if namespace is not None:
+            args.append(namespace)
+            extra_filter = " AND namespace = ?"
+        else:
+            extra_filter = " AND namespace IS NULL"
+        extra_filter = f"{extra_filter} AND name = ?"
+        args.append(name)
+        raw_hits = exec_query(index_conn,
+                              f"SELECT cve_id, type, namespace, name, vers FROM cve_index where purl_prefix = ? OR (type = ?{extra_filter});",
+                              args)
+        filtered_list = _filter_hits(raw_hits, version)
+        if with_data:
+            return get_cve_data(db_conn, filtered_list, purl)
+        return filtered_list
+
+
+def search_by_cve(cve_id: str, with_data=False) -> list | None:
+    """Search by CVE"""
+    db_conn, index_conn = db6.get(read_only=True)
+    raw_hits = exec_query(index_conn, "SELECT cve_id, type, namespace, name, vers FROM cve_index where cve_id = ?", (cve_id, ))
+    filtered_list = _filter_hits(raw_hits, "*")
+    if with_data:
+        return get_cve_data(db_conn, filtered_list, cve_id)
+    return filtered_list
+
+
+def exec_query(conn, query: str, args: tuple[str, ...]) -> list:
+    res = conn.execute(query, args)
+    return res.fetchall()
diff --git a/vdb/lib/storage.py b/vdb/lib/storage.py
deleted file mode 100644
index 08b5dc8..0000000
--- a/vdb/lib/storage.py
+++ /dev/null
@@ -1,110 +0,0 @@
-import os.path
-
-import msgpack
-
-from vdb.lib import config
-from vdb.lib.utils import chunk_list, parse_cpe, serialize_vuln_list
-
-BATCH_WRITE_SIZE = config.BATCH_WRITE_SIZE
-
-MAX_BUFFER_SIZE = config.MAX_BUFFER_SIZE
-
-
-def store(datas, db_file=config.VDB_BIN_FILE, index_file=config.VDB_BIN_INDEX):
-    """Store data in the table
-
-    :param datas: Data list to store
-    :param db_file: DB file to use
-    :param index_file: Index file to use
-    """
-    data_list = serialize_vuln_list(datas)
-    packed_obj = None
-    for batch in chunk_list(data_list, BATCH_WRITE_SIZE):
-        index_list = []
-        for data in batch:
-            if data["details"]["cpe_uri"]:
-                vendor, _, _, _ = parse_cpe(data["details"]["cpe_uri"])
-                if vendor:
-                    index_list.append(
-                        {
-                            "id": data.get("id"),
-                            "vendor": vendor.lower(),
-                            "name": data["details"]["package"].lower(),
-                            "mii": data["details"].get("mii"),
-                            "mai": data["details"].get("mai"),
-                            "mie": data["details"].get("mie"),
-                            "mae": data["details"].get("mae"),
-                        }
-                    )
-        packed_obj = msgpack.packb(batch, use_bin_type=True)
-        with open(db_file, mode="ab") as fp:
-            store_pos = fp.tell()
-            fp.write(packed_obj)
-            store_end_pos = fp.tell()
-            index_obj = msgpack.packb(
-                [
-                    {
-                        "store_pos": store_pos,
-                        "store_end_pos": store_end_pos,
-                        "index_list": index_list,
-                    }
-                ],
-                use_bin_type=True,
-            )
-            with open(index_file, mode="ab") as ifp:
-                ifp.write(index_obj)
-    return packed_obj
-
-
-def stream_read(db_file=config.VDB_BIN_FILE):
-    """"""
-    data_list = []
-    if not os.path.isfile(db_file):
-        return data_list
-    with open(db_file, mode="rb") as fp:
-        unpacker = msgpack.Unpacker(
-            fp,
-            use_list=1,
-            strict_map_key=False,
-            raw=False,
-            max_buffer_size=MAX_BUFFER_SIZE,
-        )
-        for unpacked in unpacker:
-            if unpacked:
-                data_list += unpacked
-    return data_list
-
-
-def stream_bulk_search(match_list, key_func, db_file=config.VDB_BIN_FILE):
-    """"""
-    res = []
-    store_end_pos = None
-    with open(db_file, mode="rb") as fp:
-        for amatch in match_list:
-            tmp_a = amatch.split("|")
-            if len(tmp_a) >= 3:
-                tmp_b = tmp_a[0].split("_")
-                store_pos = tmp_b[0]
-                store_end_pos = None
-                if len(tmp_b) > 1:
-                    store_end_pos = tmp_b[1]
-                    if store_end_pos.isdigit():
-                        store_end_pos = int(store_end_pos)
-                if store_pos and store_pos.isdigit():
-                    fp.seek(int(store_pos))
-            unpacker = msgpack.Unpacker(
-                fp,
-                use_list=1,
-                strict_map_key=False,
-                raw=False,
-                max_buffer_size=MAX_BUFFER_SIZE,
-            )
-            for unpacked in unpacker:
-                if isinstance(unpacked, list):
-                    for data in unpacked:
-                        if isinstance(data, dict) and key_func(data, [amatch]):
-                            res.append([data, amatch])
-                # Break if we are scanning past the storage size
-                if store_end_pos and fp.tell() > store_end_pos:
-                    break
-    return res
diff --git a/vdb/lib/utils.py b/vdb/lib/utils.py
index c9681b2..53a53c8 100644
--- a/vdb/lib/utils.py
+++ b/vdb/lib/utils.py
@@ -4,13 +4,14 @@
 import string
 from datetime import date, datetime
 from enum import Enum
+from hashlib import blake2b
 
 from cvss import CVSS3
 from packageurl import PackageURL
 from semver import VersionInfo
 
 from vdb.lib import CPE_FULL_REGEX, PackageIssue, Severity, VulnerabilityOccurrence
-from vdb.lib.config import placeholder_fix_version
+from vdb.lib.config import PLACEHOLDER_FIX_VERSION, PLACEHOLDER_EXCLUDE_VERSION, VENDOR_TO_VERS_SCHEME
 
 DATE_FORMAT_STR = "%Y-%m-%dT%H:%M:%S"
 
@@ -129,13 +130,13 @@ def _dump(obj2, path):
                 d["value"] = obj2.strftime(DATE_FORMAT_STR)
             return d
         elif (
-            isinstance(obj2, str)
-            or isinstance(obj2, bytes)
-            or isinstance(obj2, int)
-            or isinstance(obj2, float)
-            or isinstance(obj2, complex)
-            or isinstance(obj2, bool)
-            or type(obj2).__name__ == "NoneType"
+                isinstance(obj2, str)
+                or isinstance(obj2, bytes)
+                or isinstance(obj2, int)
+                or isinstance(obj2, float)
+                or isinstance(obj2, complex)
+                or isinstance(obj2, bool)
+                or type(obj2).__name__ == "NoneType"
         ):
             return obj2
         else:
@@ -329,7 +330,7 @@ def convert_to_semver(version):
                         break
 
     ver = VersionInfo(**ver)
-    rest = match.string[match.end() :]  # noqa:E203
+    rest = match.string[match.end():]  # noqa:E203
     number_part = convert_to_num(rest)
     date_obj = None
     if "-" in version:
@@ -365,23 +366,23 @@ def check_epoch(s):
     if m and m.group("epoch"):
         return True
     if (
-        ":" in s
-        or "ubuntu" in s
-        or "deb" in s
-        or "mint" in s
-        or "amzn" in s
-        or "oracle" in s
+            ":" in s
+            or "ubuntu" in s
+            or "deb" in s
+            or "mint" in s
+            or "amzn" in s
+            or "oracle" in s
     ):
         return True
     return False
 
 
 def is_hash_mode(
-    compare_ver,
-    min_version,
-    max_version,
-    mie,
-    mae,
+        compare_ver,
+        min_version,
+        max_version,
+        mie,
+        mae,
 ):
     return (
         check_hex(compare_ver)
@@ -393,11 +394,11 @@ def is_hash_mode(
 
 
 def is_epoch_mode(
-    compare_ver,
-    min_version,
-    max_version,
-    mie,
-    mae,
+        compare_ver,
+        min_version,
+        max_version,
+        mie,
+        mae,
 ):
     return (
         check_epoch(compare_ver)
@@ -419,11 +420,11 @@ def trim_epoch_colon(s):
 
 
 def trim_epoch(
-    compare_ver,
-    min_version,
-    max_version,
-    mie,
-    mae,
+        compare_ver,
+        min_version,
+        max_version,
+        mie,
+        mae,
 ):
     if check_epoch(compare_ver):
         compare_ver = trim_epoch_colon(compare_ver)
@@ -444,13 +445,42 @@ def trim_epoch(
     )
 
 
+def vers_compare(compare_ver: str | int | float, vers: str) -> bool:
+    """Purl vers based version comparison"""
+    min_version, max_version, min_excluding, max_excluding = None, None, None, None
+    if vers == "*" or compare_ver is None:
+        return True
+    if vers.startswith("vers:"):
+        vers_parts = vers.split("/")[-1].split("|")
+        if len(vers_parts) == 1:
+            single_version = vers_parts[0].strip().replace(" ", "")
+            # Handle wildcard and the special placeholder fix version
+            if single_version in (PLACEHOLDER_FIX_VERSION, "*"):
+                return True
+            # Handle unaffected case
+            if single_version.startswith("!=") and single_version.removeprefix("!=") == compare_ver:
+                return False
+        else:
+            for apart in vers_parts:
+                apart = apart.strip().replace(" ", "")
+                if apart.startswith(">="):
+                    min_version = apart.removeprefix(">=")
+                elif apart.startswith(">"):
+                    min_excluding = apart.removeprefix(">")
+                if apart.startswith("<="):
+                    max_version = apart.removeprefix("<=")
+                elif apart.startswith("<"):
+                    max_excluding = apart.removeprefix("<")
+    return version_compare(compare_ver, min_version, max_version, min_excluding, max_excluding)
+
+
 def version_compare(
-    compare_ver,
-    min_version,
-    max_version,
-    mie=None,
-    mae=None,
-):
+        compare_ver: str | int | float,
+        min_version: str | int | float,
+        max_version: str | int | float,
+        mie: str | int | float | None = None,
+        mae: str | int | float | None = None,
+) -> bool:
     """Function to check if the given version is between min and max version
 
     >>> utils.version_compare("3.0.0", "2.0.0", "2.7.9.4")
@@ -463,7 +493,7 @@ def version_compare(
     True
     """
     # Handle placeholder fix version
-    if mae == placeholder_fix_version and compare_ver:
+    if mae == PLACEHOLDER_FIX_VERSION and compare_ver:
         return True
     # Fix min versions that are erroneously sent as *
     if min_version and min_version == "*" and not mie and mae and "." in mae:
@@ -503,15 +533,15 @@ def version_compare(
         )
         # 1.10-0ubuntu4 < 1.10-0ubuntu4.1
         if (
-            tcompare_ver == tmax_version
-            or tcompare_ver == tmae
-            or (max_version and max_version.startswith(compare_ver))
-            or (mae and mae.startswith(compare_ver))
+                tcompare_ver == tmax_version
+                or tcompare_ver == tmae
+                or (max_version and max_version.startswith(compare_ver))
+                or (mae and mae.startswith(compare_ver))
         ):
             if (
-                max_version
-                and max_version.startswith(compare_ver)
-                and max_version != compare_ver
+                    max_version
+                    and max_version.startswith(compare_ver)
+                    and max_version != compare_ver
             ):
                 return True
             if mae and mae.startswith(compare_ver) and mae != compare_ver:
@@ -534,7 +564,7 @@ def version_compare(
             ubuntu_mode_detected = True
             tmpmv = None
             if max_version and (
-                "ubuntu" in max_version or "-" in max_version or "deb" in max_version
+                    "ubuntu" in max_version or "-" in max_version or "deb" in max_version
             ):
                 index_to_use = 0
                 if max_version.startswith(tmpcv_arr[0]):
@@ -568,7 +598,7 @@ def version_compare(
                 compare_ver = compare_ver.split("-")[0]
             # If after splitting the versions are equal return False
             if (max_version and compare_ver == max_version) or (
-                mae and compare_ver == mae
+                    mae and compare_ver == mae
             ):
                 return False
             if mae:
@@ -604,10 +634,10 @@ def version_compare(
         min_version = "0"
     # If compare_ver is semver compatible and min_version is * then max_version should be semver compatible
     if (
-        compare_ver
-        and VersionInfo.is_valid(compare_ver)
-        and (not min_version or min_version == "*")
-        and not VersionInfo.is_valid(max_version)
+            compare_ver
+            and VersionInfo.is_valid(compare_ver)
+            and (not min_version or min_version == "*")
+            and not VersionInfo.is_valid(max_version)
     ):
         return False
     # Perform semver match once we have all the required versions
@@ -620,12 +650,12 @@ def version_compare(
             return min_check and max_check
         # Are we dealing with dates?
         if compare_ver.startswith("20") and (
-            min_version.startswith("20") or max_version.startswith("20")
+                min_version.startswith("20") or max_version.startswith("20")
         ):
             # Compare_ver has dots where as min and max do not.
             # So we convert all of them to dates
             if (
-                "." not in min_version or "." not in max_version
+                    "." not in min_version or "." not in max_version
             ) and "." in compare_ver:
                 compare_ver = compare_ver.replace(".", "")
                 min_version = min_version.replace(".", "")
@@ -653,13 +683,13 @@ def version_compare(
             ret_check = min_check and max_check
             # openssl checks: 1.1.1n > 1.1.1g
             if (
-                ret_check
-                and max_semver
-                and compare_semver.major == max_semver.major
-                and compare_semver.minor == max_semver.minor
-                and compare_semver.patch == max_semver.patch
-                and compare_semver.prerelease == max_semver.prerelease
-                and compare_semver.build != max_semver.build
+                    ret_check
+                    and max_semver
+                    and compare_semver.major == max_semver.major
+                    and compare_semver.minor == max_semver.minor
+                    and compare_semver.patch == max_semver.patch
+                    and compare_semver.prerelease == max_semver.prerelease
+                    and compare_semver.build != max_semver.build
             ):
                 try:
                     ret_check = int(compare_semver.build) < int(max_semver.build)
@@ -695,7 +725,7 @@ def version_compare(
     # Simple case
     if not compare_ver_build and not min_version_build and not max_version_build:
         if (compare_ver == min_version and not is_min_exclude) or (
-            compare_ver == max_version and not is_max_exclude
+                compare_ver == max_version and not is_max_exclude
         ):
             return True
     compare_ver_parts = str(compare_ver).split(".")
@@ -711,29 +741,29 @@ def version_compare(
     min_version_num = normalise_num(min_version, normal_ver_len)
     max_version_num = normalise_num(max_version, normal_ver_len)
     if (
-        not max_version_num
-        and compare_ver_num > min_version_num
-        and not mie
-        and not mae
-        and not hash_mode_detected
+            not max_version_num
+            and compare_ver_num > min_version_num
+            and not mie
+            and not mae
+            and not hash_mode_detected
     ):
         return True
     # If all versions follow proper versioning then perform a simple numerical comparison
     if (
-        len(compare_ver_parts) == len(min_version_parts)
-        and len(compare_ver_parts) == len(max_version_parts)
-        and len(str(compare_ver_num)) == len(str(min_version_num))
-        and len(str(compare_ver_num)) == len(str(max_version_num))
+            len(compare_ver_parts) == len(min_version_parts)
+            and len(compare_ver_parts) == len(max_version_parts)
+            and len(str(compare_ver_num)) == len(str(min_version_num))
+            and len(str(compare_ver_num)) == len(str(max_version_num))
     ):
         if min_version_num <= compare_ver_num <= max_version_num:
             if (compare_ver_build == min_version_build and not is_min_exclude) or (
-                compare_ver_build == max_version_build and not is_max_exclude
+                    compare_ver_build == max_version_build and not is_max_exclude
             ):
                 return True
             if not compare_ver_build and (min_version_build or max_version_build):
                 if (
-                    compare_ver_num == min_version_num
-                    and compare_ver_num == max_version_num
+                        compare_ver_num == min_version_num
+                        and compare_ver_num == max_version_num
                 ):
                     return False
                 if max_version_build and compare_ver_num == max_version_num:
@@ -759,37 +789,37 @@ def version_compare(
 
     for i in range(0, normal_len):
         if (
-            not compare_ver_parts[i].isdigit()
-            or not min_version_parts[i].isdigit()
-            or not max_version_parts[i].isdigit()
+                not compare_ver_parts[i].isdigit()
+                or not min_version_parts[i].isdigit()
+                or not max_version_parts[i].isdigit()
         ):
             if (
-                compare_ver_parts[i] == min_version_parts[i]
-                and compare_ver_parts[i] == max_version_parts[i]
+                    compare_ver_parts[i] == min_version_parts[i]
+                    and compare_ver_parts[i] == max_version_parts[i]
             ):
                 continue
             else:
                 return False
         elif int(compare_ver_parts[i]) >= int(min_version_parts[i]) and int(
-            compare_ver_parts[i]
+                compare_ver_parts[i]
         ) <= int(max_version_parts[i]):
             continue
         elif int(compare_ver_parts[i]) < int(min_version_parts[i]) or int(
-            compare_ver_parts[i]
+                compare_ver_parts[i]
         ) > int(max_version_parts[i]):
             if i == 0:
                 return False
             if i == 1 and int(compare_ver_parts[i - 1]) <= int(
-                max_version_parts[i - 1]
+                    max_version_parts[i - 1]
             ):
                 return False
             if i >= 2 and int(compare_ver_parts[i - 1]) == int(
-                max_version_parts[i - 1]
+                    max_version_parts[i - 1]
             ):
                 return False
     # This will remove false positives where the comparison version is an excluded version
     if (is_min_exclude and compare_ver == min_version) or (
-        is_max_exclude and compare_ver == max_version
+            is_max_exclude and compare_ver == max_version
     ):
         return False
     if hash_mode_detected:
@@ -837,11 +867,11 @@ def get_default_cve_data(severity):
     if severity == "LOW":
         score = 2.0
         attack_complexity = "HIGH"
-        vector_string = "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N"
+        vector_string = "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:N/A:N"
     elif severity in ["MODERATE", "MODERATE", "MEDIUM"]:
         score = 5.0
         severity = "MEDIUM"
-        vector_string = "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L"
+        vector_string = "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:L/A:L"
     elif severity == "HIGH":
         score = 7.5
         attack_complexity = "LOW"
@@ -849,7 +879,7 @@ def get_default_cve_data(severity):
     return score, severity, vector_string, attack_complexity
 
 
-def get_cvss3_from_vector(vector):
+def get_cvss3_from_vector(vector: str) -> dict:
     """
     Return CVE metadata for the given vector
     :param vector: Vector
@@ -951,32 +981,33 @@ def convert_md_references(md_text):
     return ref_list
 
 
-def parse_purl(purl_str):
+def parse_purl(purl_str: str) -> dict:
     """Method to parse a package url string safely"""
     purl_obj = None
-    try:
-        purl_obj = PackageURL.from_string(purl_str).to_dict() if purl_str else None
-        # For golang, move everything to name since there is no concept of namespace
-        if purl_obj and purl_obj.get("type") == "golang" and purl_obj.get("namespace"):
-            purl_obj["name"] = f'{purl_obj["namespace"]}/{purl_obj["name"]}'
-            purl_obj["namespace"] = ""
-    except ValueError:
-        # Ignore errors
-        pass
-    if not purl_obj and purl_str:
-        tmp_a = purl_str.split("@")[0]
-        purl_obj = {}
-        if tmp_a:
-            tmp_b = tmp_a.split("/")
-            if tmp_b:
-                if len(tmp_b) < 2:
-                    purl_obj["name"] = tmp_b[-1].lower()
-                    purl_obj["namespace"] = tmp_b[0].split(":")[-1]
-                if len(tmp_b) > 2:
-                    namespace = tmp_b[-2]
-                    if tmp_b[-2].startswith("pkg:"):
-                        namespace = tmp_b[-2].split(":")[-1]
-                    purl_obj["namespace"] = namespace
+    if purl_str and purl_str.startswith("pkg:"):
+        try:
+            purl_obj = PackageURL.from_string(purl_str).to_dict() if purl_str else None
+            # For golang, move everything to name since there is no concept of namespace
+            if purl_obj and purl_obj.get("type") == "golang" and purl_obj.get("namespace"):
+                purl_obj["name"] = f'{purl_obj["namespace"]}/{purl_obj["name"]}'
+                purl_obj["namespace"] = ""
+        except ValueError:
+            # Ignore errors
+            pass
+        if not purl_obj and purl_str:
+            tmp_a = purl_str.split("@")[0]
+            purl_obj = {}
+            if tmp_a:
+                tmp_b = tmp_a.split("/")
+                if tmp_b:
+                    if len(tmp_b) < 2:
+                        purl_obj["name"] = tmp_b[-1].lower()
+                        purl_obj["namespace"] = tmp_b[0].split(":")[-1]
+                    if len(tmp_b) > 2:
+                        namespace = tmp_b[-2]
+                        if tmp_b[-2].startswith("pkg:"):
+                            namespace = tmp_b[-2].split(":")[-1]
+                        purl_obj["namespace"] = namespace
     return purl_obj
 
 
@@ -1000,7 +1031,7 @@ def convert_score_severity(score):
 
 def chunk_list(lst, size):
     for i in range(0, len(lst), size):
-        yield lst[i : i + size]
+        yield lst[i: i + size]
 
 
 def compress_str(s):
@@ -1016,3 +1047,52 @@ def decompress_str(s):
         except Exception:
             return s
     return s
+
+
+def to_purl_vers(vendor: str, versions: list) -> str:
+    vers_list = []
+    scheme = VENDOR_TO_VERS_SCHEME.get(vendor, vendor)
+    for aversion in versions:
+        if isinstance(aversion, dict):
+            version = aversion.get("version")
+            less_than = aversion.get("lessThan")
+            less_than_or_equal = aversion.get("lessThanOrEqual")
+            status = aversion.get("status")
+        else:
+            version = aversion.version.model_dump(mode="python")
+            less_than = aversion.lessThan.model_dump(mode="python") if aversion.lessThan else None
+            less_than_or_equal = aversion.lessThanOrEqual.model_dump(
+                mode="python") if aversion.lessThanOrEqual else None
+            status = str(aversion.status.value)
+        if status == "unaffected" and version:
+            vers_list.append(f"!={version}")
+        else:
+            if version and version != "0" and version != "*":
+                # Placeholder versions require special treatments
+                if version in (PLACEHOLDER_FIX_VERSION, PLACEHOLDER_EXCLUDE_VERSION):
+                    vers_list.append(version)
+                    continue
+                elif version == "0.0.0" and less_than_or_equal == "*":
+                    vers_list.append("*")
+                    continue
+                elif version == less_than_or_equal:
+                    vers_list.append(version)
+                    continue
+                else:
+                    vers_list.append(f">={version}")
+            if less_than and less_than != "*" and not less_than_or_equal:
+                vers_list.append(f"<{less_than}")
+            if not less_than and less_than_or_equal:
+                if less_than_or_equal == "*":
+                    vers_list.append("*")
+                else:
+                    vers_list.append(f"<={less_than_or_equal}")
+
+    return f"vers:{scheme}/{'|'.join(vers_list)}" if vers_list else None
+
+
+def calculate_hash(content: str, digest_size=16) -> str:
+    """Function to calculate has using blake2b algorithm"""
+    h = blake2b(digest_size=digest_size)
+    h.update(content.encode())
+    return h.hexdigest()


Test patch:
diff --git a/test/data/CVE-2023-52426.json b/test/data/CVE-2023-52426.json
new file mode 100644
index 0000000..b7661e8
--- /dev/null
+++ b/test/data/CVE-2023-52426.json
@@ -0,0 +1,95 @@
+{
+  "id": "CVE-2023-52426",
+  "sourceIdentifier": "cve@mitre.org",
+  "published": "2024-02-04T20:15:46.120",
+  "lastModified": "2024-02-09T02:02:39.800",
+  "vulnStatus": "Analyzed",
+  "descriptions": [
+    {
+      "lang": "en",
+      "value": "libexpat through 2.5.0 allows recursive XML Entity Expansion if XML_DTD is undefined at compile time."
+    },
+    {
+      "lang": "es",
+      "value": "libexpat hasta 2.5.0 permite la expansión recursiva de entidades XML si XML_DTD no está definido en el momento de la compilación."
+    }
+  ],
+  "metrics": {
+    "cvssMetricV31": [
+      {
+        "source": "nvd@nist.gov",
+        "type": "Primary",
+        "cvssData": {
+          "version": "3.1",
+          "vectorString": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H",
+          "attackVector": "LOCAL",
+          "attackComplexity": "LOW",
+          "privilegesRequired": "LOW",
+          "userInteraction": "NONE",
+          "scope": "UNCHANGED",
+          "confidentialityImpact": "NONE",
+          "integrityImpact": "NONE",
+          "availabilityImpact": "HIGH",
+          "baseScore": 5.5,
+          "baseSeverity": "MEDIUM"
+        },
+        "exploitabilityScore": 1.8,
+        "impactScore": 3.6
+      }
+    ]
+  },
+  "weaknesses": [
+    {
+      "source": "nvd@nist.gov",
+      "type": "Primary",
+      "description": [
+        {
+          "lang": "en",
+          "value": "CWE-776"
+        }
+      ]
+    }
+  ],
+  "configurations": [
+    {
+      "nodes": [
+        {
+          "operator": "OR",
+          "negate": false,
+          "cpeMatch": [
+            {
+              "vulnerable": true,
+              "criteria": "cpe:2.3:a:libexpat_project:libexpat:*:*:*:*:*:*:*:*",
+              "matchCriteriaId": "1C50909D-8A18-484B-A7DB-7EF4CA67C2CB",
+              "versionEndIncluding": "2.5.0"
+            }
+          ]
+        }
+      ]
+    }
+  ],
+  "references": [
+    {
+      "url": "https://cwe.mitre.org/data/definitions/776.html",
+      "source": "cve@mitre.org",
+      "tags": [
+        "Technical Description"
+      ]
+    },
+    {
+      "url": "https://github.com/libexpat/libexpat/commit/0f075ec8ecb5e43f8fdca5182f8cca4703da0404",
+      "source": "cve@mitre.org",
+      "tags": [
+        "Patch",
+        "Vendor Advisory"
+      ]
+    },
+    {
+      "url": "https://github.com/libexpat/libexpat/pull/777",
+      "source": "cve@mitre.org",
+      "tags": [
+        "Vendor Advisory"
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/test/data/osv-git.json b/test/data/osv-git.json
new file mode 100644
index 0000000..f056e82
--- /dev/null
+++ b/test/data/osv-git.json
@@ -0,0 +1,214 @@
+{
+  "id": "CVE-2016-0647",
+  "severity": [
+    {
+      "type": "CVSS_V3",
+      "score": "CVSS:3.0/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H"
+    }
+  ],
+  "details": "Unspecified vulnerability in Oracle MySQL 5.5.48 and earlier, 5.6.29 and earlier, and 5.7.11 and earlier and MariaDB before 5.5.49, 10.0.x before 10.0.25, and 10.1.x before 10.1.14 allows local users to affect availability via vectors related to FTS.",
+  "affected": [
+    {
+      "package": {
+        "name": "mariadb",
+        "ecosystem": "Alpine:v3.2",
+        "purl": "pkg:apk/alpine/mariadb?arch=source"
+      },
+      "ranges": [
+        {
+          "type": "ECOSYSTEM",
+          "events": [
+            {
+              "introduced": "0"
+            },
+            {
+              "fixed": "5.5.51-r0"
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "package": {
+        "name": "mariadb",
+        "ecosystem": "Alpine:v3.3",
+        "purl": "pkg:apk/alpine/mariadb?arch=source"
+      },
+      "ranges": [
+        {
+          "type": "ECOSYSTEM",
+          "events": [
+            {
+              "introduced": "0"
+            },
+            {
+              "fixed": "10.1.17-r0"
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "ranges": [
+        {
+          "type": "GIT",
+          "repo": "https://github.com/mariadb/server",
+          "events": [
+            {
+              "introduced": "5a6300dcc45da2d6c2b046560da0580548354b93"
+            },
+            {
+              "fixed": "e7061f7e5a96c66cb2e0bf46bec7f6ff35801a69"
+            },
+            {
+              "introduced": "776555af021e917ce0d6235386b43ae59fdd5161"
+            },
+            {
+              "introduced": "c235de12ae3723b96944337bd89ad9cc87f21d8f"
+            }
+          ]
+        },
+        {
+          "type": "GIT",
+          "repo": "https://github.com/mysql/mysql-server",
+          "events": [
+            {
+              "introduced": "863a73b80b83801a14b416006e64cf892837a657"
+            },
+            {
+              "fixed": "e7061f7e5a96c66cb2e0bf46bec7f6ff35801a69"
+            }
+          ]
+        }
+      ]
+    }
+  ],
+  "references": [
+    {
+      "type": "ADVISORY",
+      "url": "http://rhn.redhat.com/errata/RHSA-2016-0705.html"
+    },
+    {
+      "type": "ADVISORY",
+      "url": "http://rhn.redhat.com/errata/RHSA-2016-1480.html"
+    },
+    {
+      "type": "ADVISORY",
+      "url": "http://rhn.redhat.com/errata/RHSA-2016-1481.html"
+    },
+    {
+      "type": "ADVISORY",
+      "url": "http://rhn.redhat.com/errata/RHSA-2016-1602.html"
+    },
+    {
+      "type": "ADVISORY",
+      "url": "http://www.debian.org/security/2016/dsa-3557"
+    },
+    {
+      "type": "ADVISORY",
+      "url": "http://www.debian.org/security/2016/dsa-3595"
+    },
+    {
+      "type": "ADVISORY",
+      "url": "http://www.oracle.com/technetwork/security-advisory/cpuapr2016v3-2985753.html"
+    },
+    {
+      "type": "ADVISORY",
+      "url": "http://www.oracle.com/technetwork/topics/security/linuxbulletinjul2016-3090544.html"
+    },
+    {
+      "type": "ADVISORY",
+      "url": "http://www.securityfocus.com/bid/86495"
+    },
+    {
+      "type": "ADVISORY",
+      "url": "http://www.securitytracker.com/id/1035606"
+    },
+    {
+      "type": "ADVISORY",
+      "url": "http://www.ubuntu.com/usn/USN-2953-1"
+    },
+    {
+      "type": "ADVISORY",
+      "url": "http://www.ubuntu.com/usn/USN-2954-1"
+    },
+    {
+      "type": "ADVISORY",
+      "url": "https://access.redhat.com/errata/RHSA-2016:1132"
+    },
+    {
+      "type": "ADVISORY",
+      "url": "https://mariadb.com/kb/en/mariadb/mariadb-10025-release-notes/"
+    },
+    {
+      "type": "ADVISORY",
+      "url": "https://mariadb.com/kb/en/mariadb/mariadb-10114-release-notes/"
+    },
+    {
+      "type": "ADVISORY",
+      "url": "https://mariadb.com/kb/en/mariadb/mariadb-5549-release-notes/"
+    },
+    {
+      "type": "ARTICLE",
+      "url": "http://lists.opensuse.org/opensuse-security-announce/2016-05/msg00035.html"
+    },
+    {
+      "type": "ARTICLE",
+      "url": "http://lists.opensuse.org/opensuse-security-announce/2016-05/msg00053.html"
+    },
+    {
+      "type": "ARTICLE",
+      "url": "http://lists.opensuse.org/opensuse-security-announce/2016-06/msg00033.html"
+    },
+    {
+      "type": "ARTICLE",
+      "url": "http://lists.opensuse.org/opensuse-security-announce/2016-06/msg00034.html"
+    },
+    {
+      "type": "ARTICLE",
+      "url": "http://lists.opensuse.org/opensuse-security-announce/2016-06/msg00051.html"
+    },
+    {
+      "type": "FIX",
+      "url": "http://www.oracle.com/technetwork/security-advisory/cpuapr2016v3-2985753.html"
+    },
+    {
+      "type": "WEB",
+      "url": "http://lists.opensuse.org/opensuse-security-announce/2016-05/msg00035.html"
+    },
+    {
+      "type": "WEB",
+      "url": "http://lists.opensuse.org/opensuse-security-announce/2016-05/msg00053.html"
+    },
+    {
+      "type": "WEB",
+      "url": "http://lists.opensuse.org/opensuse-security-announce/2016-06/msg00033.html"
+    },
+    {
+      "type": "WEB",
+      "url": "http://lists.opensuse.org/opensuse-security-announce/2016-06/msg00034.html"
+    },
+    {
+      "type": "WEB",
+      "url": "http://lists.opensuse.org/opensuse-security-announce/2016-06/msg00051.html"
+    },
+    {
+      "type": "WEB",
+      "url": "http://lists.opensuse.org/opensuse-security-announce/2016-06/msg00053.html"
+    },
+    {
+      "type": "WEB",
+      "url": "http://www-01.ibm.com/support/docview.wss?uid=isg3T1024168"
+    },
+    {
+      "type": "WEB",
+      "url": "http://www.securityfocus.com/bid/86495"
+    },
+    {
+      "type": "WEB",
+      "url": "http://www.securitytracker.com/id/1035606"
+    }
+  ],
+  "modified": "2019-12-27T16:08:55Z",
+  "published": "2016-04-21T10:59:15Z"
+}
diff --git a/test/test_db.py b/test/test_db.py
deleted file mode 100644
index 0cb7f32..0000000
--- a/test/test_db.py
+++ /dev/null
@@ -1,198 +0,0 @@
-import json
-import os
-import tempfile
-
-import pytest
-
-from vdb.lib import db as db
-from vdb.lib.gha import GitHubSource
-from vdb.lib.nvd import NvdSource
-from vdb.lib.utils import parse_cpe
-
-
-@pytest.fixture
-def test_db():
-    with tempfile.NamedTemporaryFile(delete=False) as fp:
-        with tempfile.NamedTemporaryFile(delete=False) as indexfp:
-            return db.get(db_file=fp.name, index_file=indexfp.name)
-
-
-@pytest.fixture
-def test_vuln_data():
-    test_cve_data = os.path.join(
-        os.path.dirname(os.path.realpath(__file__)), "data", "cve_data.json"
-    )
-    with open(test_cve_data, "r") as fp:
-        json_data = json.loads(fp.read())
-        nvdlatest = NvdSource()
-        return nvdlatest.convert(json_data)
-
-
-@pytest.fixture
-def test_gha_data():
-    test_cve_data = os.path.join(
-        os.path.dirname(os.path.realpath(__file__)), "data", "gha_data.json"
-    )
-    with open(test_cve_data, "r") as fp:
-        json_data = json.loads(fp.read())
-        ghalatest = GitHubSource()
-        return ghalatest.convert(json_data)[0]
-
-
-def test_create(test_db, test_vuln_data):
-    docs = db.store(test_db, test_vuln_data)
-    assert len(docs) > len(test_vuln_data)
-
-
-@pytest.mark.skip(reason="Slow test")
-def test_search_slow(test_db, test_vuln_data):
-    table = test_db
-    docs = db.list_all(table)
-    assert len(docs) == 0
-    docs = db.store(test_db, test_vuln_data)
-    assert len(docs) > 0
-    all_data = db.list_all(table)
-    assert all_data
-    for d in all_data:
-        res = db.pkg_search(
-            table,
-            d["details"]["package"],
-            d["details"]["mai"],
-        )
-        assert len(res)
-        assert res[0].to_dict()["package_issue"]
-
-
-def test_search_fast(test_db, test_vuln_data):
-    table = test_db
-    docs = db.list_all(table)
-    assert len(docs) == 0
-    docs = db.store(test_db, test_vuln_data)
-    assert len(docs) > 0
-    all_data = db.list_all(table)
-    assert all_data
-    search_list = [
-        {
-            "name": d["details"]["package"],
-            "version": d["details"]["mai"],
-        }
-        for d in all_data
-    ]
-    res = db.bulk_index_search(search_list)
-    assert len(res)
-
-
-def test_gha_create(test_db, test_gha_data):
-    docs = db.store(test_db, test_gha_data)
-    assert len(docs) > len(test_gha_data)
-
-
-def test_gha_search_slow(test_db, test_gha_data):
-    table = test_db
-    docs = db.list_all(table)
-    assert len(docs) == 0
-    docs = db.store(test_db, test_gha_data)
-    assert len(docs) > 0
-    all_data = db.list_all(table)
-    assert all_data
-    for d in all_data:
-        version = d["details"]["mai"]
-        if version and version != "*":
-            res = db.pkg_search(
-                table,
-                d["details"]["package"],
-                version,
-            )
-            assert len(res)
-            assert res[0].to_dict()["package_issue"]
-
-
-def test_gha_vendor_search(test_db, test_gha_data):
-    table = test_db
-    docs = db.list_all(table)
-    assert len(docs) == 0
-    docs = db.store(test_db, test_gha_data)
-    assert len(docs) > 0
-    all_data = db.list_all(table)
-    assert all_data
-    for d in all_data:
-        vendor, _, _, cve_type = parse_cpe(d["details"]["cpe_uri"])
-        version = d["details"]["mai"]
-        if version and version != "*":
-            res = db.vendor_pkg_search(
-                table,
-                vendor,
-                d["details"]["package"],
-                version,
-            )
-            assert len(res)
-            assert res[0].to_dict()["package_issue"]
-
-
-def test_gha_search_bulk(test_db, test_gha_data):
-    table = test_db
-    docs = db.list_all(table)
-    assert len(docs) == 0
-    docs = db.store(test_db, test_gha_data)
-    assert len(docs) > 0
-    all_data = db.list_all(table)
-    assert all_data
-    tmp_list = [
-        {
-            "name": d["details"]["package"],
-            "version": d["details"]["mai"],
-        }
-        for d in all_data
-        if d["details"]["mai"] != "*"
-    ]
-    res = db.bulk_index_search(tmp_list)
-    assert len(res)
-
-
-def test_index_search(test_db, test_vuln_data):
-    # This slow test ensures that every data in the main database is indexed
-    table = test_db
-    docs = db.list_all(table)
-    assert len(docs) == 0
-    docs = db.store(test_db, test_vuln_data)
-    assert len(docs) > 0
-    all_data = db.list_all(table)
-    assert all_data
-    tmp_list = []
-    for d in all_data[:40]:
-        version = d["details"]["mai"]
-        if version and version != "*":
-            tmp_list.append({"name": d["details"]["package"], "version": version})
-    res = db.bulk_index_search(tmp_list)
-    assert len(res)
-    for r in res:
-        name_ver = r.split("|")
-        fullres = db.index_search(name_ver[1], name_ver[2])
-        assert fullres
-
-
-def test_vendor_index_search(test_db, test_vuln_data):
-    # This slow test ensures that every data in the main database is indexed
-    table = test_db
-    docs = db.list_all(table)
-    assert len(docs) == 0
-    docs = db.store(test_db, test_vuln_data)
-    assert len(docs) > 0
-    all_data = db.list_all(table)
-    assert all_data
-    tmp_list = []
-    for d in all_data[:40]:
-        vendor, _, _, cve_type = parse_cpe(d["details"]["cpe_uri"])
-        tmp_list.append(
-            {
-                "vendor": vendor,
-                "name": d["details"]["package"],
-                "version": d["details"]["mai"],
-            }
-        )
-    res = db.bulk_index_search(tmp_list)
-    assert len(res)
-    for r in res:
-        name_ver = r.split("|")
-        fullres = db.index_search(name_ver[2], name_ver[3])
-        assert fullres
diff --git a/test/test_source.py b/test/test_source.py
index 24dd7ed..5ff4282 100644
--- a/test/test_source.py
+++ b/test/test_source.py
@@ -65,6 +65,15 @@ def test_nvd_api_json4():
         return json.loads(fp.read())
 
 
+@pytest.fixture
+def test_nvd_api_git_json():
+    test_cve_data = os.path.join(
+        os.path.dirname(os.path.realpath(__file__)), "data", "CVE-2023-52426.json"
+    )
+    with open(test_cve_data, "r") as fp:
+        return json.loads(fp.read())
+
+
 @pytest.fixture
 def test_osv_rust_json():
     test_cve_data = os.path.join(
@@ -146,6 +155,15 @@ def test_osv_mevents_json():
         return json.loads(fp.read())
 
 
+@pytest.fixture
+def test_osv_git_json():
+    test_cve_data = os.path.join(
+        os.path.dirname(os.path.realpath(__file__)), "data", "osv-git.json"
+    )
+    with open(test_cve_data, "r") as fp:
+        return json.loads(fp.read())
+
+
 @pytest.fixture
 def test_aqua_alsa_json():
     test_cve_data = os.path.join(
@@ -385,7 +403,7 @@ def test_convert(test_cve_json):
             assert detail.package
             assert detail.package_type
     cvesource = CVESource()
-    data = cvesource.convert(data)
+    data = cvesource.convert5(data)
     assert len(data) == 384
 
 
@@ -402,11 +420,11 @@ def test_convert2(test_cve_wconfig_json):
             assert detail.package_type
             assert not detail.fixed_location
     cvesource = CVESource()
-    data = cvesource.convert(data)
+    data = cvesource.convert5(data)
     assert len(data) == 1
 
 
-def test_nvd_api_convert(test_nvd_api_json1, test_nvd_api_json2, test_nvd_api_json3, test_nvd_api_json4):
+def test_nvd_api_convert(test_nvd_api_json1, test_nvd_api_json2, test_nvd_api_json3, test_nvd_api_json4, test_nvd_api_git_json):
     nvdlatest = NvdSource()
     data = nvdlatest.convert(test_nvd_api_json1)
     assert len(data) == 1
@@ -421,14 +439,17 @@ def test_nvd_api_convert(test_nvd_api_json1, test_nvd_api_json2, test_nvd_api_js
     data = nvdlatest.convert(test_nvd_api_json2)
     assert len(data) == 1
     cvesource = CVESource()
-    data = cvesource.convert(data)
+    data = cvesource.convert5(data)
     assert len(data) == 1
     data = nvdlatest.convert(test_nvd_api_json3)
     assert len(data) == 0
-    data = cvesource.convert(data)
+    data = cvesource.convert5(data)
     assert len(data) == 0
     data = nvdlatest.convert(test_nvd_api_json4)
     assert len(data) == 1
+    data = nvdlatest.convert(test_nvd_api_git_json)
+    assert len(data) == 1
+    assert len(data[0].details) == 2
 
 
 @pytest.mark.skip(reason="This downloads and tests with live data")
@@ -500,8 +521,12 @@ def test_osv_convert(
     test_osv_swift_json,
     test_osv_swift2_json,
     test_osv_mevents_json,
+    test_osv_git_json,
 ):
     osvlatest = OSVSource()
+    cve_data = osvlatest.convert(test_osv_git_json)
+    assert cve_data
+    assert len(cve_data) == 5
     cve_data = osvlatest.convert(test_osv_mevents_json)
     assert cve_data
     assert len(cve_data) == 4
diff --git a/test/test_storage.py b/test/test_storage.py
deleted file mode 100644
index a00593a..0000000
--- a/test/test_storage.py
+++ /dev/null
@@ -1,42 +0,0 @@
-import json
-import os
-import tempfile
-
-import pytest
-
-from vdb.lib import storage as storage
-from vdb.lib.gha import GitHubSource
-from vdb.lib.nvd import NvdSource
-
-
-@pytest.fixture
-def test_vuln_data():
-    test_cve_data = os.path.join(
-        os.path.dirname(os.path.realpath(__file__)), "data", "cve_data.json"
-    )
-    with open(test_cve_data, "r") as fp:
-        json_data = json.loads(fp.read())
-        nvdlatest = NvdSource()
-        return nvdlatest.convert(json_data)
-
-
-@pytest.fixture
-def test_gha_data():
-    test_cve_data = os.path.join(
-        os.path.dirname(os.path.realpath(__file__)), "data", "gha_data.json"
-    )
-    with open(test_cve_data, "r") as fp:
-        json_data = json.loads(fp.read())
-        ghalatest = GitHubSource()
-        return ghalatest.convert(json_data)[0]
-
-
-def test_create(test_vuln_data):
-    with tempfile.NamedTemporaryFile(delete=False) as fp:
-        data = storage.store(test_vuln_data, db_file=fp.name)
-        assert data
-        fp.flush()
-
-        datas = storage.stream_read(db_file=fp.name)
-        assert len(datas) > len(test_vuln_data)
-        fp.close()
diff --git a/test/test_utils.py b/test/test_utils.py
index d6404f9..2fbce0c 100644
--- a/test/test_utils.py
+++ b/test/test_utils.py
@@ -574,12 +574,12 @@ def test_redhat_build_compare():
 v0.3.7 0.3.8
 """
     for line in data.split("\n"):
-        tmpA = line.split(" ")
-        if len(tmpA) != 2:
+        tmp_a = line.split(" ")
+        if len(tmp_a) != 2:
             continue
-        res = utils.version_compare(tmpA[0], 0, "*", None, tmpA[1])
+        res = utils.version_compare(tmp_a[0], 0, "*", None, tmp_a[1])
         assert res
-        res = utils.version_compare(tmpA[1], 0, "*", None, tmpA[0])
+        res = utils.version_compare(tmp_a[1], 0, "*", None, tmp_a[0])
         assert not res
     res = utils.version_compare("14.20.1", 0, "*", None, "12")
     assert not res
@@ -616,21 +616,14 @@ def test_version_len():
 
 
 def test_fix_test():
-    assert (
-        utils.fix_text(
-            "Unauthenticated crypto and weak IV in Magento\\Framework\\Encryption"
-        )
-        == "Unauthenticated crypto and weak IV in Magento Framework Encryption"
-    )
+    assert utils.fix_text("Unauthenticated crypto and weak IV in Magento\\Framework\\Encryption") == "Unauthenticated crypto and weak IV in Magento Framework Encryption"
 
 
 def test_convert_md_references():
     assert utils.convert_md_references(
         "- [Issue #60](https://github.com/tj/node-growl/issues/60)\n- [PR #61](https://github.com/tj/node-growl/pull/61)"
-    ) == [
-        {"name": "Issue #60", "url": "https://github.com/tj/node-growl/issues/60"},
-        {"name": "PR #61", "url": "https://github.com/tj/node-growl/pull/61"},
-    ]
+    ) == [{"name": "Issue #60", "url": "https://github.com/tj/node-growl/issues/60"},
+          {"name": "PR #61", "url": "https://github.com/tj/node-growl/pull/61"}]
 
 
 def test_ubuntu_versions():
@@ -655,9 +648,7 @@ def test_ubuntu_versions():
 
 
 def test_parse_purl():
-    assert utils.parse_purl(
-        "pkg:maven/org.jenkins-ci.plugins/JDK_Parameter_Plugin"
-    ) == {
+    assert utils.parse_purl("pkg:maven/org.jenkins-ci.plugins/JDK_Parameter_Plugin") == {
         "type": "maven",
         "namespace": "org.jenkins-ci.plugins",
         "name": "JDK_Parameter_Plugin",
@@ -698,3 +689,34 @@ def test_parse_purl():
         "subpath": None,
     }
     assert utils.parse_purl("pkg:gem/remote:@https:%2F%2Frubygems.org%2F") == {}
+
+
+def test_purl_vers_convert():
+    assert utils.to_purl_vers("almalinux", [
+        {"version": "0", "status": "affected", "versionType": "semver", "lessThan": "2.3.4"}]) == "vers:rpm/<2.3.4"
+    assert utils.to_purl_vers("almalinux", [{"version": "0", "status": "affected", "versionType": "semver",
+                                             "lessThan": "5.14.21-150500.13.5.1"}]) == "vers:rpm/<5.14.21-150500.13.5.1"
+    assert utils.to_purl_vers("gem", [{'version': '0.1.1', 'status': 'affected', 'versionType': 'gem',
+                                       'lessThanOrEqual': '4.3.5'}]) == "vers:gem/>=0.1.1|<=4.3.5"
+    test_tuples = [
+        ("npm", [{'version': '0.0.0', 'status': 'affected', 'versionType': 'npm', 'lessThanOrEqual': '*'}],
+         "vers:npm/*"),
+        ("npm", [{'version': '0.0.0', 'status': 'affected', 'versionType': 'npm', 'lessThan': '1.1.3'}],
+         "vers:npm/>=0.0.0|<1.1.3"),
+        ("npm", [{'version': '0.0.0', 'status': 'affected', 'versionType': 'npm', 'lessThan': '3.5.0'}],
+         "vers:npm/>=0.0.0|<3.5.0"),
+        ("swift", [{'version': '4.0.0-rc.2.5', 'status': 'affected', 'versionType': 'semver', 'lessThan': '4.29.4'}],
+         "vers:swift/>=4.0.0-rc.2.5|<4.29.4"),
+        ("swift", [{'version': '0.0.0', 'status': 'affected', 'versionType': 'semver', 'lessThan': '4.60.3'}],
+         "vers:swift/>=0.0.0|<4.60.3"),
+        ("swift", [{'version': '0.0.0', 'status': 'affected', 'versionType': 'semver', 'lessThan': '4.60.3'}],
+         "vers:swift/>=0.0.0|<4.60.3"),
+        ("maven", [{'version': '8.5.0', 'status': 'affected', 'versionType': 'maven', 'lessThan': '8.5.94'}],
+         "vers:maven/>=8.5.0|<8.5.94"),
+        ("maven", [{'version': '11.0.0-M1', 'status': 'affected', 'versionType': 'maven', 'lessThan': '11.0.0-M12'}],
+         "vers:maven/>=11.0.0-M1|<11.0.0-M12"),
+        ("debian", [{'version': '88.88.8', 'status': 'affected', 'versionType': 'maven'}], "vers:deb/88.88.8"),
+        ("debian", [{'version': '53.5', 'status': 'affected', 'versionType': 'maven', 'lessThanOrEqual': '53.5'}], "vers:deb/53.5"),
+    ]
+    for tt in test_tuples:
+        assert utils.to_purl_vers(tt[0], tt[1]) == tt[2]